Behold Your Vector Nightmare, Amiga Fans

 title=
Over on Hacker News there was an entry about the “awful” Amiga Kickstart 1.x icon and why it looked the way it did. This led to a link over on Stack Overflow where it was revealed that this was graphic actually drawn in a vector style, as opposed to raster pixels.

They also provided the actual bytes used to render the graphic. I thought it’d be fun to write a little parser to render these bytes.

And so did several other people, apparently, as I’m discovering now. :|

Anyway, here’s my humble Codepen. And if that goes down, the original code is below the embed. 😎

(I skipped flood fill because I used two.js for this, and didn’t realize until it was too late that I’d chosen poorly. Easy enough to swap out the graphics library, but it’s time to move on.)

See the Pen Amiga Kickstart vector parser by Toby D (@Fortyseven) on CodePen.

/*
Inspired by:
https://retrocomputing.stackexchange.com/questions/13897/why-was-the-kickstart-1-x-insert-floppy-graphic-so-bad/13901

2021-03-06
\*/


const floppy = [
0xff, 0x01, 0x23, 0x0b, 0x3a, 0x0b, 0x3a, 0x21, 0x71, 0x21, 0x71, 0x0b, 0x7d,
0x0b, 0x88, 0x16, 0x88, 0x5e, 0x7f, 0x5e, 0x7f, 0x38, 0x40, 0x38, 0x3e, 0x36,
0x35, 0x36, 0x34, 0x38, 0x2d, 0x38, 0x2d, 0x41, 0x23, 0x48, 0x23, 0x0b, 0xfe,
0x02, 0x25, 0x45, 0xff, 0x01, 0x21, 0x48, 0x21, 0x0a, 0x7e, 0x0a, 0x8a, 0x16,
0x8a, 0x5f, 0x56, 0x5f, 0x56, 0x64, 0x52, 0x6c, 0x4e, 0x71, 0x4a, 0x74, 0x44,
0x7d, 0x3c, 0x81, 0x3c, 0x8c, 0x0a, 0x8c, 0x0a, 0x6d, 0x09, 0x6d, 0x09, 0x51,
0x0d, 0x4b, 0x14, 0x45, 0x15, 0x41, 0x19, 0x3a, 0x1e, 0x37, 0x21, 0x36, 0x21,
0x36, 0x1e, 0x38, 0x1a, 0x3a, 0x16, 0x41, 0x15, 0x45, 0x0e, 0x4b, 0x0a, 0x51,
0x0a, 0x6c, 0x0b, 0x6d, 0x0b, 0x8b, 0x28, 0x8b, 0x28, 0x76, 0x30, 0x76, 0x34,
0x72, 0x34, 0x5f, 0x32, 0x5c, 0x32, 0x52, 0x41, 0x45, 0x41, 0x39, 0x3e, 0x37,
0x3b, 0x37, 0x3e, 0x3a, 0x3e, 0x41, 0x3d, 0x42, 0x36, 0x42, 0x33, 0x3f, 0x2a,
0x46, 0x1e, 0x4c, 0x12, 0x55, 0x12, 0x54, 0x1e, 0x4b, 0x1a, 0x4a, 0x17, 0x47,
0x1a, 0x49, 0x1e, 0x4a, 0x21, 0x48, 0xff, 0x01, 0x32, 0x3d, 0x34, 0x36, 0x3c,
0x37, 0x3d, 0x3a, 0x3d, 0x41, 0x36, 0x41, 0x32, 0x3d, 0xff, 0x01, 0x33, 0x5c,
0x33, 0x52, 0x42, 0x45, 0x42, 0x39, 0x7d, 0x39, 0x7d, 0x5e, 0x34, 0x5e, 0x33,
0x5a, 0xff, 0x01, 0x3c, 0x0b, 0x6f, 0x0b, 0x6f, 0x20, 0x3c, 0x20, 0x3c, 0x0b,
0xff, 0x01, 0x60, 0x0e, 0x6b, 0x0e, 0x6b, 0x1c, 0x60, 0x1c, 0x60, 0x0e, 0xfe,
0x03, 0x3e, 0x1f, 0xff, 0x01, 0x62, 0x0f, 0x69, 0x0f, 0x69, 0x1b, 0x62, 0x1b,
0x62, 0x0f, 0xfe, 0x02, 0x63, 0x1a, 0xff, 0x01, 0x2f, 0x39, 0x32, 0x39, 0x32,
0x3b, 0x2f, 0x3f, 0x2f, 0x39, 0xff, 0x01, 0x29, 0x8b, 0x29, 0x77, 0x30, 0x77,
0x35, 0x72, 0x35, 0x69, 0x39, 0x6b, 0x41, 0x6b, 0x41, 0x6d, 0x45, 0x72, 0x49,
0x72, 0x49, 0x74, 0x43, 0x7d, 0x3b, 0x80, 0x3b, 0x8b, 0x29, 0x8b, 0xff, 0x01,
0x35, 0x5f, 0x35, 0x64, 0x3a, 0x61, 0x35, 0x5f, 0xff, 0x01, 0x39, 0x62, 0x35,
0x64, 0x35, 0x5f, 0x4a, 0x5f, 0x40, 0x69, 0x3f, 0x69, 0x41, 0x67, 0x3c, 0x62,
0x39, 0x62, 0xff, 0x01, 0x4e, 0x5f, 0x55, 0x5f, 0x55, 0x64, 0x51, 0x6c, 0x4e,
0x70, 0x49, 0x71, 0x46, 0x71, 0x43, 0x6d, 0x43, 0x6a, 0x4e, 0x5f, 0xff, 0x01,
0x44, 0x6a, 0x44, 0x6d, 0x46, 0x70, 0x48, 0x70, 0x4c, 0x6f, 0x4d, 0x6c, 0x49,
0x69, 0x44, 0x6a, 0xff, 0x01, 0x36, 0x68, 0x3e, 0x6a, 0x40, 0x67, 0x3c, 0x63,
0x39, 0x63, 0x36, 0x65, 0x36, 0x68, 0xff, 0x01, 0x7e, 0x0b, 0x89, 0x16, 0x89,
0x5e, 0xfe, 0x01, 0x22, 0x0b, 0xfe, 0x01, 0x3b, 0x0b, 0xfe, 0x01, 0x61, 0x0f,
0xfe, 0x01, 0x6a, 0x1b, 0xfe, 0x01, 0x70, 0x0f, 0xfe, 0x01, 0x7e, 0x5e, 0xfe,
0x01, 0x4b, 0x60, 0xfe, 0x01, 0x2e, 0x39, 0xff, 0xff,
];

class AmigaVectParser {
constructor(bytes, elem) {
this.palette = ["#FFFFFF", "#000000", "#7777CC", "#BBBBBB"];
this.offset = [0, 0];
this.prevOffset = [0, 0];
this.curColor = 0;
this.isDrawing = false;
this.buffer = bytes || [0xff, 0xff];
this.done = false;

this.two = new Two({ width: 640, height: 400 }).appendTo(elem);
}

doCmd(cmd_pair) {
if (cmd_pair[0] === 0xff) {
this.isDrawing = false;
if (cmd_pair[1] === 0xff) {
// cmd_done
this.done = true;
return;
} else {
// cmd_colorSet
this.curColor = cmd_pair[1];
return;
}
} else if (cmd_pair[0] === 0xfe) {
// cmd_floodFill
this.isDrawing = false;
this.pointer += 2; //TODO FLOOD FILL
return;
}
if (!this.isDrawing) {
// first coordinate in a poly-line
this.prevOffset[0] = cmd_pair[0];
this.prevOffset[1] = cmd_pair[1];
this.isDrawing = true;
return;
} else {
// continuing the poly-line
this.offset[0] = cmd_pair[0];
this.offset[1] = cmd_pair[1];

let line = this.two.makeLine(
this.prevOffset[0] * 2,
this.prevOffset[1] * 2, // doubling up X/Y to make it easier to see at 640x400
this.offset[0] * 2,
this.offset[1] * 2
);

line.stroke = this.palette[this.curColor];
line.linewidth = 1;

this.prevOffset[0] = this.offset[0];
this.prevOffset[1] = this.offset[1];
}
}

draw() {
let cmd = [0, 0];
let pointer = 0;
this.done = false;

while (!this.done) {
cmd[0] = this.buffer[pointer++];
cmd[1] = this.buffer[pointer++];
this.doCmd(cmd);
}
this.two.update();
}
}

renderer = new AmigaVectParser(floppy, document.getElementById("draw-shapes"));
renderer.draw();