XXIIVV
Zoe Suit 0.1 picture
20I12 — Zoe Suit 0.1

The zoe file format is designed to store binary picture data.

The format was designed to make it easier to read and write uncompressed pixel data for monochromatic pictures, more specifically to help move noodle pictures from and to the macintosh system.

Header

In a zoe file, the first 4 bytes are the header, followed by the bitmap data representing every pixel. The width of the image is stored in the 4th byte as factor of 8, and the maximum image width is 2032. For example, a picture with a size of 512x342, will have the following header:

hexasciimeaning
5aZmagic number
4fO
45E
40width(64px)
..bitmap data

Body

The following picture data has a width of 4 pixels, and its total bitmap data size is 16 bits, or 2 bytes.

1010
0011
1100
0101

The series of 16 pixels are represented linearly as follows:

hexA3C5
bin1010001111000101

A 16x16 picture is expected to be 32 bytes of pixel data and 4 bytes of header, and so a 32x32 picture is expected to be 132 bytes, a 512x512 picture is expected to be about 33kb(32772 bytes) in size, and so on.

.gif.zoe
5a4f 4504 ffff ffff 80ff ffff 80ff 003f
80fc 100f 80f8 c707 80f9 2117 80f1 21c3
88f4 2043 88f0 1273 88f1 0803 80f2 8003
80f5 4c43 80f2 921b 80f1 121b 80f0 4cfb
80f4 00fb 80f0 00c3 80f3 c7d3 8ff1 c7c3
83f0 c607 83e4 4687 83c0 060f 8380 003f
93ff ffff 8fff 7fff 83f7 efff 83fd bfff
83ff ffff 83c0 07ff 83e2 afff 83f1 5fff
ffff ffff 
5a4f 4504 0c3f ffff 1440 0002 2480 0004
4500 0008 87ff fff0 8e00 0028 9fff ffc4
a000 0082 c000 0101 ffff fe02 8000 0304
8000 028c 8000 0254 8929 2224 8aaa a204
8aaa a204 8929 2204 8000 0204 8000 0204
8a4a 4204 8aaa a204 8aaa a204 8a4a 4204
8000 0204 8000 0204 8491 2208 8aaa a210
8aaa a220 8491 2240 8000 0280 8000 0300
ffff fe00 

Javascript Implementation

The implementation used in Noodle.

// Convert context to

function zoeRead (ctx, w, h, x, y, pad = 4) {
  const head = [90, 79, 69, Math.floor(w / 8)]
  const body = new Uint8Array(pad + (w * h) / 8)
  const data = ctx.getImageData(x || 0, y || 0, w, h).data
  const vals = [128, 64, 32, 16, 8, 4, 2, 1]
  for (let i = 0, n = data.length; i < n; i += 4) {
    const key = pad + Math.floor(i / 32)
    const rgb = [data[i], data[i + 1], data[i + 2]]
    body[key] += vals[(i / 4) % 8] * (lum(rgb) < 127 ? 1 : 0)
  }
  head.forEach((e, i) => { body[i] = e })
  return body
}

function zoeWrite (ctx, byteArray, pad = 4) {
  const w = byteArray[pad - 1] * 8
  const vals = [128, 64, 32, 16, 8, 4, 2, 1]
  for (let i = pad; i < byteArray.length * 8; i++) {
    const byte = byteArray[pad + Math.floor((i-pad) / 8)]
    const mask = vals[(i-pad) % 8]
    ctx.fillStyle = (byte & mask) !== 0 ? 'black' : 'white'
    ctx.fillRect((i-pad) % w, Math.floor((i-pad)/w), 1, 1)
  }
}

// Find the luminance of a color.

function lum (pixel) {
  return Math.floor(0.2126 * pixel[0] + 0.7152 * pixel[1] + 0.0722 * pixel[2])
}

Pascal Implementation

The full THINK Pascal project can be found here.

procedure DrawFile (contents: Ptr; fileSize: Longint);
  var
   bmap: BitMap;
   headerPtr: ^Longint;
   header: Longint;
   width, height: Integer;
 begin
  bmap.baseAddr := nil;
  headerPtr := Pointer(contents);
  header := headerPtr^;
  if BitAnd(header, $FFFFFF00) <> $5a4f4500 then
   begin
    WriteLn('Header mismatch');
    CleanupAndHalt;
   end;
  width := Integer(BitAnd(header, $000000FF));
  height := (fileSize - 4) div width;
  SetRect(bmap.bounds, 0, 0, width * 8, height);
  bmap.rowBytes := width;
  bmap.baseAddr := Pointer(Longint(contents) + 4);
  CopyBits(bmap, thePort^.portBits, bmap.bounds, bmap.bounds, srcCopy, nil);
 end;

Found a mistake? Submit an edit to zoe format.

Last update on 20I12, edited 4 times. +17/26fh