Bitmap Sequence Format

   Cybiko bitmaps sequences are files that consist of a header structure and one or more bitmap structures. "Single bitmaps" are just sequences that consist of a header and single bitmap structure. The following is general structure of the bitmap sequence:

Field type Field name Description
byte_t h_bits bits per pixel (currently 1 or 2)
byte_t h_count number of bitmaps in the file (1..255)
byte_t h_width original frame width
byte_t h_height original frame height
bitmap_t h_body[] <h_count> bitmaps

Each stored bitmap (represented as structure) has the following organization:
Field type Field name Description
byte_t b_xorg x-origin, in pixels
byte_t b_yorg y-origin, in pixels
byte_t b_width raster width, in pixels
byte_t b_height raster height, in pixels
byte_t b_raster[] scanlines, the raster itself

where each raster has individual pixels packed as shown in pic_format.bmp.
Here is how both sequence and bitmap headers are represented internally:

  typedef unsigned char byte_t;

  struct header_t
  {
    byte_t   h_bits;      // bits per pixel (currently 1 or 2)
    byte_t   h_count;     // number of bitmaps in the file (1..255)
    byte_t   h_width;     // original width
    byte_t   h_height;    // original height
    bitmap_t h_body[ 1 ]; // bitmaps
  };

  struct bitmap_t
  {
    byte_t b_xorg;        // x-origin, pels
    byte_t b_yorg;        // y-origin, pels
    byte_t b_width;       // raster width, pels
    byte_t b_height;      // raster height, pels
    byte_t b_raster[ 1 ]; // scanlines
  };
And here is how rasters are stored (i.e. how b_raster[] is formed; process of loading must be just the opposite). Please note that (x1, y1, x2, y2) is a non-transparent rectangle of original frame (0, 0, header_t::h_width, header_t::h_height).
  x1 = bitmap_t::b_xorg;
  y1 = bitmap_t::b_yorg;
  x2 = x1 + bitmap_t::b_width - 1;
  y2 = y1 + bitmap_t::b_height - 1;
  total_bits = header_t::h_bits;

  for( y = y1; y <= y2; y ++ )
  {
    bit = 7;
    byte = 0;

    for( x = x1; x <= x2; x ++ )
    {
      pixel = "pixel at x, y"

      byte |= total_bits == 1 ? ( pixel == P_BLACK ) << bit:
        (( pixel ^ 3 ) & 3 ) << bit - 1;

      bit -= total_bits;

      if( bit < 0 ) putc( byte, file ), bit = 7, byte = 0;
    }
    if( bit != 7 ) putc( byte, file );
  }

At last, here is how rasters are loaded by emulator. Although it addresses loading, this example is probably less useful than previous, because code in emulator converts 1- and 2-bit pixels into bytes, on the fly, for more simple and effective processing. Cybiko applications are not supposed to behave this way, i.e. they must not expand rasters.
  x1 = bitmap_t::b_xorg;
  y1 = bitmap_t::b_yorg;
  x2 = x1 + bitmap_t::b_width - 1;
  y2 = y1 + bitmap_t::b_height - 1;
  total_bits = header_t::h_bits;
  bit = 0 // current bit, 1..8;
  byte = 0 // cyrrent packet;

  int get_bit( FILE* file, int& bit, int& byte )
  {
    if( bit == 0 ) byte = (byte_t) getc( file ), bit = 8;

    if( byte == EOF ) die( "get_bit: premature end-of-file" );

    return ( byte & 1 << -- bit ) != 0;
  }

  for( y = y1; y <= y1; y ++ )
  {
    for( x = x1; x <= x2; x ++ )
    {
      pixel = 0;

      for( b = 0; b < bits; b ++ )
      {
        pixel <<= 1;

        pixel += get_bit( file, bit, byte );
      }
      switch( pixel )
      {
        case 0: pixel = clr_white; break;

        case 1: pixel = bits == 1 ? clr_black: clr_ltgray; break;

        case 2: pixel = clr_dkgray; break;

        default: pixel = clr_black;
      }
      "pixel at x, y" = pixel;
    }
    bit = 0; // abandon trailing bits ( end of scanline )
  }