Jump to content
  • entry
    1
  • comments
    0
  • views
    4,455

What is an XEF file?


simonl

1,061 views

Just in case anyone's interested, in my posting here, the attached program is capable of exporting compressed images. This is useful as a full screen VBXE standard resolution would be 80640 bytes if not compressed so it's a waste of disk/cartridge space. Currently, it uses deflate on 8kb blocks, although it will not compress a block if the compressed data comes out longer. This typically gives a 1/3 reduction in file size, but files from Amiga, C64 etc. give much better compression ratios due to the smaller palette count.

 

For instance, this 320x200 picture would be 62.5k uncompressed:

 

blogentry-12834-125302744774.png

 

The PNG file is 30k, and the corresponding XEF file is 26.3k (I had to change the extension to .zip to upload):

 

dotc.zip

 

This format is currently in the less than useful position of not having an associated viewer on the Atari (although they can be previewed through the application, choose File | Open XeFlate...). I'm working on this at the moment, but if anyone would like to save me the trouble ;) or comment on the format I'm working on, here are the details.

 

The file is structured as follows:

 

***************
* File Header *
***************
Name              Length (Bytes)        Comment
----------------- --------------------- --------------------------------------------
Bits Per Pixel    1                     Either 4 or 8 depending on the mode
Graphics Mode     1                     Values:
                                         0x1F = VBXE standard
                                         0x2F = VBXE low resolution
                                         0x3F = VBXE high resolution
Image Width       2*                    Width in bytes 
Image Height      2*                    Height in scan lines
Block Count       2*                    The number of blocks of data in the file
Palette Length    2*                    The number of palette entries in the file. If zero, the
                                       laoo.act palette can be assumed if the palette is not 
                                       specified elsewhere.
Palette Entries   3 x [Palette Length]  The palette entries as (R, G, B) three byte tuples

* Low byte first

Then, there are [block Count] blocks, as follows:

*********
* Block *
*********

N.B. The data is currently compressed so that each block is <= 8kb in length when uncompresssed,
due to the 8k bank switching arrangement used by VBXE this seemed expecient!

Name              Length (Bytes)        Comment
----------------- --------------------- --------------------------------------------
Compression Type  1                     The compression method used:
                                         0x00 = None
                                         0x01 = Deflate
                                       Further methods will be added e.g. RLE  
Compressed Length 2*                    The length of the block's data, in bytes,
                                       not including the 3 block header bytes.
Data              [Compressed Length]   The block's data                                                                         

* Low byte first

 

The following C# snippet illustrates loading an XEF file into a Windows Bitmap:

 

     // Read the file header

     // Mode
     byte bitsPerPixel = r.ReadByte();
     byte graphicsMode = r.ReadByte();

     // Size
     ushort imageWidthBytes = (ushort)(r.ReadByte() | (r.ReadByte() << );
     ushort imageHeightScanLines = (ushort)(r.ReadByte() | (r.ReadByte() << );

     int width = imageWidthBytes * (8 / bitsPerPixel);
     int height = imageHeightScanLines;

     byte blockCount = r.ReadByte();
     
     // Palette (if specified - otherwise, assume the default VBXE palette)
     ushort paletteLength = (ushort)(r.ReadByte() | (r.ReadByte() << );
     Color[] palette = null;

     if (paletteLength > 0)
     {
       palette = new Color[paletteLength];

       for (int color = 0; color < paletteLength; color++)
       {
         byte red = r.ReadByte();
         byte green = r.ReadByte();
         byte blue = r.ReadByte();

         palette[color] = Color.FromArgb(red, green, blue);
       }
     }
     else
     {
       // Otheriwse, use whatever colors we've currently got loaded
       palette = Atari.Colors.BrushColors;
     }

     int x = 0;
     int y = 0;

     Bitmap result = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

     // Read the blocks
     for (int block = 0; block < blockCount; block++)
     {
       // Get the compressed length
       byte blockCompressionType = r.ReadByte();
       ushort blockLength = (ushort)(r.ReadByte() | (r.ReadByte() << );

       System.Diagnostics.Debug.WriteLine(string.Format("Block {0} -> {1} bytes ({2})", block + 1, blockLength, Enum.ToObject(typeof(BlockCompressionType), blockCompressionType)));
          
       byte[] compressed = r.ReadBytes(blockLength);
       byte[] decompressed = null;
       int decompressedLength = 0;
       
       // On Atari - switch in the correct bank from VBXE memory and decompress to there

       switch (blockCompressionType)
       {
         case 0x00:
           // Uncompressed - copy straight to VBXE bank
           decompressed = compressed;
           decompressedLength = blockLength;
           break;
         case 0x01:
           // Deflate - http://atariarea.krap.pl/x-asm/inflate.html
           decompressed = new byte[0x2000];
           using (MemoryStream ms = new MemoryStream(compressed))
           {
             ms.Position = 0;
             using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress, true))
             {
               decompressedLength = ds.Read(decompressed, 0, 0x2000);
               ds.Close();
             }
           }
           break;
       }

       #region Plot the data on the bitmap (obviously redundant on Atari!)

       for (int i = 0; i < decompressedLength; i++)
       {
         result.SetPixel(x, y, palette[decompressed[i]]);

         x++;
         if (x >= width)
         {
           x = 0;
           y++;
         }
       }

       #endregion
     }

     #region Set the pixel aspect ratio (or set the XDL on Atari!)

     switch (graphicsMode)
     {
       case 0x1F:
         // Vbxe standard
         break;
       case 0x2F:
         #region Vbxe Low Resolution - rescale to 2 x width
         Bitmap scaledUpBitmap = new Bitmap(result.Width * 2, result.Height, result.PixelFormat);
         using (Graphics graphics = Graphics.FromImage(scaledUpBitmap))
         {
           graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
           graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
           graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
           graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
           graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
           graphics.DrawImage(result, 0, 0, scaledUpBitmap.Width, scaledUpBitmap.Height);
         }
         result.Dispose();
         result = scaledUpBitmap;
         #endregion
         break;
       case 0x3F:
         #region VbxeHighResolution - rescale to 1/2 width (Windows doesn't like rectangular pixels)
         Bitmap scaledDownBitmap = new Bitmap(result.Width >> 1, result.Height, result.PixelFormat);
         using (Graphics graphics = Graphics.FromImage(scaledDownBitmap))
         {
           graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
           graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
           graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
           graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
           graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
           graphics.DrawImage(result, 0, 0, scaledDownBitmap.Width, scaledDownBitmap.Height);
         }
         result.Dispose();
         result = scaledDownBitmap;
         #endregion
         break;
     }

     #endregion

     return result;

0 Comments


Recommended Comments

There are no comments to display.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...