ANI formal definition

From FreeSpace Wiki
(Redirected from ANI Formal Definition)
Jump to: navigation, search

This information is copied from the Descent Developer's Network Ani specs page

Introduction

The ANI file format is similar to the 256-color-PCX compression. It was however improved to provide transparent pixels as well as of course multiple images that make together the ANImation. All original ANI files are in FREESPACE.VP in Conflict, Descent: FreeSpace. In FreeSpace 2 they have been divided over several VP files.

This document was written by Heiko Herrmann with help by John Slagel of Volition Inc. (header information) and Garry Knudson (image data).

File format

The file begins with a fixed-size header and a variable number of keyframe definitions:

//Definitions
typedef struct ani_header
{
    short        should_be_zero;      // always 0
    short        version;             // >= 2
    short        fps;                 // played with <fps> frames per second
    pal          rgb;                 // ?
    short        w,h;                 // width, height
    short        nframes;             // number of frames
    char         packer_code;         // code used for compressed (repeated) bytes
    pal          palette[256];        // rgb-palette
    short        num_keys;            // ?
} ani_header;

typedef struct ani_key
{
    short twobyte;
    short startcount;
} ani_key;


//File format
ani_header anifile.header;
for(i=0; i<anifile.header.num_keys; i++)
 ani_key anifile.key[i];
int anifile.endcount;

A keyframe is a frame that is provided *as is*, that means with no transparent pixels. So drawing that frame is not dependant of having the data from the previous frame (which is needed for transparent pixels). Usually ANImations have only one keyframe (the initial image = frame no.1). Some have more This is used for the following purposes:

The main hall ANImations have only key frames so that they can be played backwards (when moving the mouse away from a room in the main hall). The weapon/ship ANImations have 2 (one additional) key frames: the second is the so-called "loop point": everything before is the animation how the weapon/ship appears in the briefings screen, everything after is the part that loops endless - the rotation. The header is followed by the actual image data, as discussed below.


Image data

To understand the image data format, I chose to provide some code here:

// ANI reader example code
// Taken from Descent Manager ANIVIEW32 - an adaption of a code part of FSView
// Written by Garry Knudson and Heiko Herrmann
// Copyright 1998-2000 by Descent Network Team
//
// Note: the header must be loaded into the structure anifile.header.
// The image data must then be loaded into memory and available at pointer char *cf.
// The bitmap data is then loaded into anifile.frames[i].buffer

byte runvalue,runcount;
int cur,max;
byte *p,*p2;
byte flagbyte;


max=anifile.header.w;
if(anifile.header.w % 4!=0) {   //Garry Knudson 9-23-99
 max += (4-(anifile.header.w%4));
}
max=max * anifile.header.h;

byte *cur_frame=new byte[max];
byte *last_frame=new byte[max];

memset(last_frame,anifile.header.rgb.r+256*anifile.header.rgb.g+65536*anifile.header.rgb.b,max);


//Load frames 
for (i=0;i<anifile.header.nframes;i++)
{
 cf=readfrombuffer(cf,&flagbyte,1);
 cur=0;

 /*char x[256];
 sprintf(x,"Frame %i: %i\n",i,anif->GetPosition());
 OutputDebugString(x);*/

 p=cur_frame;
 if(i>0)
  p2=last_frame; //for transparent pixels
 else
  p2=cur_frame;

 int x0,y0;
 runcount=0;
 cur=0;
 for(y0=0;y0<anifile.header.h;y0++)
 {
  for(x0=0;x0<anifile.header.w;x0++)
  {
   if (runcount>0)
   {
    runcount--;
   }
   else
   {
    cf=readfrombuffer(cf,&runvalue,1);
    if(runvalue==anifile.header.packer_code)
    {
     cf=readfrombuffer(cf,&runcount,1);
     if(runcount<2)
      runvalue=anifile.header.packer_code;
     else
      cf=readfrombuffer(cf,&runvalue,1);
    }
   }
   *p=runvalue;
   if(runvalue==254) //if transparent pixel, then use the pixel from the last frame
    *p=*p2;
   p++; p2++; cur++;
  }
  if(anifile.header.w % 4!=0) {   //Garry Knudson 9-23-99
   p +=(4-(anifile.header.w%4));
   p2 +=(4-(anifile.header.w%4));
   cur +=(4-(anifile.header.w%4));
  }
 }

 //Process frame
 anifile.frames[i].buffer=new byte[max];
 memcpy(last_frame,cur_frame,max);
 memcpy(anifile.frames[i].buffer,cur_frame,max);
}


---------------

char *readfrombuffer(char *org,void *dest,int num)
{
 memcpy(dest,org,num);
 org+=num;
 return org;
}

Note to this source: the header must be loaded into the structure anifile.header. The image data must then be loaded into memory and available at pointer char *cf. The bitmap data is then loaded into anifile.frames[i].buffer.