> Ideas.Fonts1bpp

Strategy for development of the Arthur Font Manager
---------------------------------------------------

Old font formats:   x90y45       4-bpp scaleable non-compacted bitmaps

New font formats:   b9999x9999   1-bpp unscaleable run-length encoded bitmaps
                                 1-bpp unscaleable non-compacted bitmaps
                    f9999x9999   4-bpp scaleable bitmaps
                    Outlines     scaleable outlines

1-bpp forms are loaded in a compacted form: in order to paint them, they are
unpacked row-by-row into 4-bpp, and the same plotting code is used.  Note
that the row-by-row organisation facilitates the use of the same plotting
code, and also enables the 'repeat row' feature to be implemented easily.

The uncompacted 1-bpp forms are expanded row by row into 4-bpp, and simply
plotted.  The reason for this format is because certain types of character
shape can be represented more efficiently by a bitmap.  Note that the two
1-bpp forms can each occur within the same font file: other combinations of
formats are not possible (although an old font file can contain several
versions of a font at different sizes).

The outline fonts are unpacked on a character-by-character basis: target font
may be 4- or 1-bpp uncompacted bit map.  In both cases a 1-bpp sprite is
generated - in the 4-bpp case the 1-bpp sprite is generated 4x4 times larger
than required, and the Crow's super-sampling technique is used to generate
the final 4-bpp image.


Font File Formats
-----------------

Metrics:
        
        40      Name of font, terminated by <cr>
        4       16
        4       16
        1       n = number of defined characters
        3       reserved (must be 0)
        256     character mapping (ie. indices into following arrays)
        2n      x0 - bounding box of character (in 1/1000ths em)
        2n      y0 - coordinates are relative to the 'origin point'
        2n      x1 - 2 bytes per entry
        2n      y1 -
        2n      x-offset after printing this character
        2n      y-offset after printing this character


Shapes (old format) - retained for compatibility

        Filename: x90y45

        Index:
                1       point size
                1       bits per pixel (ignored)
                1       pixels per inch (x-direction)
                1       pixels per inch (y-direction)
                4       reserved (was checksum)
                4       --> pixel data (offset in file)
                4       size of pixel data
                ...
                1       0

        Pixel data:
                4       x-size in 1/16ths point * pixels per inch (x)
                4       y-size in 1/16ths point * pixels per inch (y)
                4       pixels per inch (x-direction)
                4       pixels per inch (y-direction)
                1       x0    - maximum bounding box for any character
                1       y0    - bottom-left is inclusive
                1       x1    - top-right is exclusive
                1       y1    - all coordinates are in pixels
                512     2-byte offsets from table start of character data
                        (pixel data is limited to 64K per block)

        Character data:

                1       x0      - bounding box
                1       y0
                1       x1-x0
                1       y1-y0
                4*n     4bpp, consecutive rows bottom->top
                              not aligned until the end


Shapes (new format):

        No index - each file contains 1 version of the font.
                   The filename indicates the size of the bitmap

        b9999x9999      1-bpp definition
        a9999x9999      4-bpp (anti-aliased) definition
        Outlines        the outline file

              '9999' = pixel size (ie. point size * pixels per inch / 72)
                       zero-suppressed decimal number

        The rules governing which fonts are looked for in which order depend
        on whether the font is to be displayed in 1-bpp or not.  In fact, the
        same font can have entries in the cache relating to different numbers
        of bits per pixel, since Font_FindFont does not know in advance
        whether the font is to be displayed in 1-bpp or several.

        File header:
                4       "FONT"  - identification word
                1       Bits per pixel: 0 = outlines
                                             1 = 1 bpp
                                             4 = 4 bpp
                1       Version number of file format:
                               0  first version
                               1  scaffolding added
                               2  coordinates made into 12-bit rather than 16-bit values
                               3  12-bit scaffold coords (top 4 bits = link)
                               4  bit 3 set in charflags => outlines
                2       if bpp = 0: design size of font
                        if bpp > 0: flags:
                                    bit 0 set => horizontal subpixel placement
                                    bit 1 set => vertical subpixel placement
                                      pixel chunks have 4x as many entries for
                                      each of these bits, with vertical
                                      placement more tightly bound,
                                      ie. if flags=3,
                                      char offset = 4*32*4*x-placement
                                                  + 32*4*y-placement
                                                  + (char AND 31)
                2       x0      - font bounding box (16-bit signed)
                2       y0      - units are pixels or design units
                2       x1-x0
                2       y1-y0
                4       file offset of  0..31 chunk (word-aligned)
                4       file offset of 32..63 chunk
                ...
                4       file offset of 224..255 chunk
                4       file offset of end
                        if offset(n+1)=offset(n), then chunk n is null.

     Table start:
                2      n = 2-byte offset from table start of title (ie. end of data)

     Bitmaps: (n=10 normally - other values are reserved)
                2       x-size (1/16th point)
                2       x-res (dpi)
                2       y-size (1/16th point)
                2       y-res (dpi)

     Outlines:
                510   255 x 2-byte offsets from table start of scaffold data for char
                       (0 ==> no scaffolding, and offset[0] = n = offset of end of data)
                n      sets of entries of the form:
                        1  char code of 'base' scaffold entry (0 ==> none)
                        1  8 bits, bit n set ==> x-scaffold line n is defined in the base char
                        1  8 bits, bit n set ==> y-scaffold line n is defined in the base char
                        1  8 bits, bit n set ==> x-scaffold line n is defined in the local scaffolding
                        1  8 bits, bit n set ==> y-scaffold line n is defined in the local scaffolding
                        scaffold lines of the form:
                           2  bits 0..11 = coordinate (signed)
                              bits 12..14 = scaffold link index (0 => none)
                              bit 15 set => 'linear' scaffold link (scale coords according to position)
                           1  width (254 ==> L-tangent, 255 ==> R-tangent)

                ?       description of contents of file:
                        <Font name>, 0, "Outlines", 0
                                        "999x999 point at 999x999 dpi", 0

                        ... word-aligned chunks follow

        Chunk data:
                4       offset within chunk to 1st character
                4       offset within chunk to 2nd character
                ...
                4       offset within chunk to 32nd character
                        ... word-aligned character data follows

        Char data:      (depends on file id)
                1       flags:
                           bit 0: if set, char box is 16-bit, else 8-bit
                           bit 1: if set, data is 1-bpp, else 4-bpp
                           bit 2: if set: initial pixel is black, else white
                           bit 3: if set: data is outline, else bitmap
                           bits 4..7: 'f' value for char (0 ==> not encoded)
                2/3     x0, y0      sign-extended 8- or 12- bit coordinates
                2/3     xs, ys      width, height (pixel bounding box = x0,y0,x0+xs,y0+ys)
                n       data:
                          1-bpp uncrunched: rows from bottom->top
                          4-bpp uncrunched: rows from bottom->top
                          1-bpp crunched: list of (packed) run-lengths
                          outlines: list of move/line/curve segments
                        word-aligned at the end of the character data

        The bit saying whether the char box should be 8 or 12 bits long
        seems rather silly, since it only saves 4 bytes per character, but
        the coords can be read by the same subroutine that reads in the
        coordinates within the character, if it's an outline.

Outline char format
-------------------

Here the 'pixel bounding box' is actually the bounding box of the outline in
terms of the design size of the font (in the file header).  The box is subtly
different from that supplied in the IntMetrics file, since it must be
designed slightly too large to ensure that the bitmap created to hold the
character image is large enough.

        char == item *
        item == terminator | move | line | curve
        move == mflags x y
        line == lflags x y
       curve == cflags x1 y1 x2 y2 x3 y3
           x == 12-bit number = (b0<<20 + b1<<28) >> 20
           y == 12-bit number = (b1<<16 + b2<<24) >> 20
      terminator == 1 byte, bits 0..1 = 0, bit 2 set ==> stroke path follows as well
      mflags == 1 byte, bits 0..1 = 1, bits 2..7 = scaffold attachments
      lflags == 1 byte, bits 0..1 = 2, bits 2..7 = scaffold attachments
      cflags == 1 byte, bits 0..1 = 3, bits 2..7 = scaffold attachments
       flags == bits 2..4 = horizontal scaffold line index (see scaffold info above)
                bits 5..7 = vertical scaffold line index (relates to the last point in the move/line/curve)

The character consists of a series of moves, line segments and curve
segments.  It is further subdivided into subpaths, where each subpath starts
with a 'move' command, and is terminated when another 'move' command is
encountered.  The subpath is not filled unless it is closed: ie. the last
point coincides with the first - paths following a terminator with bit 2 set
are always stroked, ie. they are not closed even if the first point does
coincide with the first.  This feature allows the inclusion of 'skeleton
lines', which ensure that even at very small sizes, all the essential parts
of a character are drawn.

The flags associated with each line segment relate to the last point
specified in the definition of that move/line/curve.


1-bpp uncompacted format
------------------------

1 bit per pixel, in rows from bottom-left to top-right, not aligned until
word-aligned at the end of the character.


1-bpp compacted data format
---------------------------

The whole character is initially treated as a stream of bits, as for the
uncompacted form.  The bit stream is then scanned row by row, with
consecutive duplicate rows being replaced by a 'repeat count', and alternate
runs of black and white pixels are noted.  The repeat counts and run counts
are then themselves encoded in a set of 4-bit entries.

The final format is thus a stream of 4-bit numbers, with the following
meanings:

<packed number> ==
          0         followed by n-1 zeroes, followed by n+1 nibbles
                    = resulting number + (13-f)*16 + f+1 - 16
   i =    1 .. f    i
   i =  f+1 .. 13   (i-f-1)*16 + next nibble + f + 1
         14         followed by n=<packed number> = repeat count of n
         15         repeat count of 1 (ie. 1 extra copy of this row)

The optimal value of f lies between 1 and 12, and must be computed
individually for each character, by scanning the data and calculating the
length of the output for each possible value.  The value yielding the
shortest result is then used, unless that is larger than the bitmap itself,
in which case the bitmap is used.

The number of nibbles used to represent different packed numbers varies as
follows:

        .. f               1 nibble        n
        .. (13-f)*16+f     2 nibbles       m n
        .. 2^8-1           3 nibbles       0 n1 n2

Larger numbers require more nibbles, but since these do not depend on f they
are not important for this calculation.

Repeat counts operate on the current row, as understood by the unpacking
algorithm, ie. at the end of the row the repeat count is used to duplicate
the row as many times as necessary.  This effectively means that the repeat
count applies to the row containing the first pixel of the next run to start
up.

Note that rows consisting of entirely white or entirely black pixels cannot
always be represented by using repeat counts, since the run may span more
than one row, so a long run count is used instead.


Compacting a 1-bpp master
-------------------------

Source: a 1-bpp sprite, plus the coordinates of the character origin.

1.  Scan the sprite to find the minimum bounding box.

2.  Scan the box, counting the run-lengths for each value of f
        Note that this scanning must look for repeated rows, as below

3.  Decide on the most efficient form of f (or don't compact the bitmap)

4.  If compacted, encode the bitmap (within the box) as follows:
        Compare each row with the next one
        If it is the same, increment the 'repeat count' and do the next one
        When a row is different, output the repeat count ...
        ... then output the current row, run-length encoded
        The last run includes the first part of the next row (except at end)


Unpacking a 1-bpp master
------------------------

Source: character data as described in the file format section above.

1.  Read char header to get x0,y0,xs,ys, f
2.  Construct rows from bottom to top:
3.  Remember repeat count as encountered (default = 0)
4.  Do run-length expansion until current row is full
5.  Output row as many times as required (depends on repeat count)
6.  Continue with next row, keeping remainder of previous run

Each row is output using the same algorithm as before, since the rows are
unpacked into 4-bpp, using colours 0 and 15.  One optimisation would be to
behave differently in 1-bpp output modes, but this is a certain amount of
effort!

