SBSPSS/Utils/Libs/GLib/Dpanim.cpp
2000-12-04 14:13:40 +00:00

1050 lines
18 KiB
C++

/*=========================================================================
DPANM.HPP
Author: Gary Liddon @ Fareham
Created:
Project:
Purpose:
Copyright (c) 1997 Gary Liddon
===========================================================================*/
/*----------------------------------------------------------------------
Includes
-------- */
/* Std Lib
------- */
//#include <stdlib.h>
#include <minmax.h>
#include <fstream>
#include <iostream>
/* Glib
---- */
/* Local
----- */
#include "dpanim.hpp"
//#include "ilbm.hpp"
/* Name space
---------- */
using namespace std;
/*----------------------------------------------------------------------
Structure defintions
-------------------- */
typedef UWORD LP_TABLE_ELEMENT;
#define MAX_NUMBER_CYCLES 16
#define BMBODY_RUNSKIPDUMP 1
#define MAX_COLORS 256
#define PALETTE_SIZE (MAX_COLORS * sizeof(long))
#define MAX_RECORDS 0xffffu
#define MAX_LARGE_PAGE 256
#define LARGE_PAGE_SIZE 0x10000L
#define LPF_HEADER_HEAD_SIZE_IN_FILE 256
#define LPF_HEADER_SIZE_IN_FILE ((sizeof(AnimHdr)) + (8 * 16) + (4 * 256) + (sizeof(LpTable[MAX_LARGE_PAGE])))
#define LP_FILE_OFFSET(nLp) ((nLp) * LARGE_PAGE_SIZE + LPF_HEADER_SIZE_IN_FILE)
#define MakeID(d,c,b,a) (((ULONG)(a)<<24l) | ((ULONG)(b)<<16l) | ((ULONG)(c)<<8l) | ((ULONG)(d)))
#define LARGE_PAGE_FILE_ID MakeID('L','P','F',' ')
#define ANIM_CONTENTS_ID MakeID('A','N','I','M')
#define FIRST_FRAME_N 1
#define FIRST_RECORD_N 0 // Records #d from 0.
/*----------------------------------------------------------------------
Typedefs
-------- */
class LpObj;
/*----------------------------------------------------------------------
Class Defintions
---------------- */
/* Compressed Frame
---------------- */
class CompFrame : public GObject
{
public:
CompFrame(void);
CompFrame(CompFrame const & C);
~CompFrame(void);
void operator=(CompFrame const &);
/* Make Vectorable */
bool operator==(CompFrame const &) const;
bool operator<(CompFrame const &) const;
void WriteByte(u8 Byte);
void WriteWord(u16 Word);
void WriteData(u8 const * Src,int Amount);
void Write(ofstream & Out) const;
friend ostream & operator<<(ostream & str,CompFrame & Fr);
u8 const * SeeData(void) const {return(BinData);}
int GetSize() const {return(Len);}
protected:
void Dump();
void Init();
void Resize(int NewLen);
void Increase(int NewLen) {Resize(ActualLen+NewLen);}
u8 * BinData;
int Len;
int ActualLen;
enum
{
RESIZE_CHUNKS=1000,
};
};
typedef std::vector<CompFrame> CompFrameVec;
/* Large Frame Object
------------------ */
class LpObj : public GObject
{
public:
LpObj(void);
bool AddFrame(CompFrame const & C );
void SetBaseRecord(int f){BaseRecord=f;}
int NumOfFrames(void) { return(MyFrames.size());}
void Write(std::ofstream & Out) const;
void WriteHdr(std::ofstream & Out) const;
/* Make Vectorable */
void operator=(LpObj const &);
bool operator==(LpObj const &) const;
bool operator<(LpObj const &) const;
protected:
int GetSizeInAll(void) const;
int BaseRecord;
CompFrameVec MyFrames;
};
/*----------------------------------------------------------------------
Function Prototypes
------------------- */
static void GenerateLpTable(LpObjVec & LpTable,CompFrameVec & CompFrames);
static int GetMaxRecsPerLp(LpObjVec & LpTable);
static void WriteWord(std::ofstream & Out,u16 Word);
/*----------------------------------------------------------------------
Vars
---- */
//static FilterDetails MyDetails(DpAnimFilter(),".anm");
/*----------------------------------------------------------------------
Data
---- */
/*----------------------------------------------------------------------
Function: Main
---------------------------------------------------------------------- */
DpAnimFilter::DpAnimFilter()
{
InitVars();
}
DpAnimFilter::DpAnimFilter(char const * FName) : GAnimFilter(FName)
{
InitVars();
loaded_LP=0xffff;
}
DpAnimFilter::~DpAnimFilter(void)
{
DeleteVars();
}
bool DpAnimFilter::Load(GAnim & Anm)
{
int Frames;
Palette MyPal;
MakeVars();
anm_init2(FileName);
GetPal(MyPal);
anm_read_first_frame();
Frames=0;
do
{
Frame * NewFr;
NewFr=&Anm.GetNewFrame();
NewFr->SetFrame(dst_buffer,320,200,MyPal);
Frames++;
}
while(anm_read_next_frame());
fclose(inANM);
DeleteVars();
return(true);
}
void DpAnimFilter::GetPal(Palette & MyPal)
{
fseek(inANM,sizeof(AnimHdr)+sizeof(ColCycles[MAX_NUMBER_CYCLES]),SEEK_SET);
for (int f=0;f<256;f++)
{
MyPal[f].SetB(fgetc(inANM));
MyPal[f].SetG(fgetc(inANM));
MyPal[f].SetR(fgetc(inANM));
fgetc(inANM);
}
}
void DpAnimFilter::InitVars(void)
{
lpfTable=NULL;
current_LP=NULL;
dst_buffer=NULL;
}
void DpAnimFilter::MakeVars(void)
{
DeleteVars();
if (!(lpfTable = new LpTable[MAX_LARGE_PAGE]))
Error(ERM_OUTOFMEM);
if (!(current_LP = new LpTableMem))
Error(ERM_OUTOFMEM);
if (!(dst_buffer=new u8[128960]))
Error(ERM_OUTOFMEM);
}
void DpAnimFilter::DeleteVars(void)
{
if (lpfTable)
delete[] lpfTable;
if (current_LP)
delete current_LP;
if (dst_buffer)
delete dst_buffer;
InitVars();
}
bool DpAnimFilter::Save(GAnim & Anm)
{
MakeVars();
lpfHdr.InitHdr();
ofstream Out;
Out.open(FileName,ios::trunc|ios::out|ios::binary);
if (!Out)
Error(ERR_FATAL,"Can't open %s for output",(char const *)FileName);
else
{
int NumOfFrames;
CompFrameVec CompFrames;
LpObjVec MyLpTable;
NumOfFrames=Anm.NumOfFrames();
if (NumOfFrames)
{
CompFrames.reserve(NumOfFrames);
CompFrames.resize(NumOfFrames);
for (int f=0;f<NumOfFrames;f++)
Encode(Anm[f],CompFrames[f]);
GenerateLpTable(MyLpTable,CompFrames);
AnimHdr MyHdr;
MyHdr.nLps=MyLpTable.size();
MyHdr.lRecords=NumOfFrames;
MyHdr.maxRecsPerLp=GetMaxRecsPerLp(MyLpTable);
MyHdr.nFrames=NumOfFrames;
WriteHdr(Out,MyHdr);
WriteCycs(Out);
WritePal(Out,Anm[0].GetPal());
WriteLpTable(Out,MyLpTable);
WriteLps(Out,MyLpTable);
DiscardAnim();
}
}
Out.close();
return(true);
}
ULONG DpAnimFilter::anm_init2(char const * filename)
{
FILE * Fp;
if(!(Fp=fopen(filename,"rb")))
Error(ERR_FATAL,"can't open %s",filename);
return(anm_init(Fp));
}
/*----------------------------------------------------------------------
Setup buffer for playback and buffer for LP.
---------------------------------------------------------------------- */
ULONG DpAnimFilter::anm_init(FILE * filename)
{
inANM = filename;
fseek(inANM,0,SEEK_SET);
fread(&lpfHdr,sizeof(AnimHdr),1,inANM);
if ((lpfHdr.id != LARGE_PAGE_FILE_ID) || (lpfHdr.contentType != ANIM_CONTENTS_ID))
Error(ERR_FATAL,"Input file: %s is not an ANM file.",filename);
fseek(inANM,lpfHdr.lpfTableOffset,SEEK_SET);
fread(lpfTable,sizeof(LpTable[MAX_LARGE_PAGE]),1,inANM);
loaded_LP = 0xffff;
return(((lpfHdr.hasLastDelta) && (lpfHdr.lastDeltaValid)) ? (lpfHdr.nFrames - 1) : (lpfHdr.nFrames));
}
/*----------------------------------------------------------------------
Read LP off disk into memory for playback.
---------------------------------------------------------------------- */
void DpAnimFilter::read_LP(UWORD LP_to_load)
{
if (LP_to_load == loaded_LP)
return;
loaded_LP = LP_to_load;
fseek(inANM,LP_FILE_OFFSET(LP_to_load),0);
fread(current_LP,(ULONG) (8l + lpfTable[LP_to_load].nRecords * 2l),1,inANM);
fread(lp_buffer,(ULONG) lpfTable[LP_to_load].nBytes,1,inANM);
}
/*----------------------------------------------------------------------
Read next frame from anm file.
---------------------------------------------------------------------- */
int DpAnimFilter::anm_read_next_frame(void)
{
int loop;
UWORD load_lp;
UWORD base;
ULONG lRecords;
/* next frame */
current_Frame++;
lRecords = lpfHdr.lRecords;
if (lpfHdr.hasLastDelta && lpfHdr.lastDeltaValid)
lRecords--;
if (current_Frame == lRecords)
return(0);
load_lp = 0;
while ((current_Frame >= (lpfTable[load_lp].baseRecord + lpfTable[load_lp].nRecords)) || (current_Frame < lpfTable[load_lp].baseRecord))
{
load_lp++;
}
read_LP(load_lp);
base = 0; /* Set start to beginning of buffer */
/* Find start of Frame by adding number of bytes of previous frames. */
for (loop = 0;loop <= (current_Frame - current_LP->baseRecord);loop++)
base += current_LP->size[loop];
if (lp_buffer[base] != 'B')
Error(ERR_FATAL,"Corrupt Anim File or Non Bitmapped image. base=%d,lp=%d in %s\n",base,load_lp,GetName());
if ((UWORD) lp_buffer[base + 2] != BMBODY_RUNSKIPDUMP)
Error(ERR_FATAL,"Compression scheme for frames is unkown.");
PlayRunSkipDump(&lp_buffer[base+4],dst_buffer);
return(1);
}
/*----------------------------------------------------------------------
Read first frame of anm file.
---------------------------------------------------------------------- */
void DpAnimFilter::anm_read_first_frame(void)
{
lp_buffer = &dst_buffer[320u * 200u];
current_Frame = FIRST_FRAME_N;
read_LP(loaded_LP);
current_Frame = 0xffff;
anm_read_next_frame();
}
/*----------------------------------------------------------------------
Encode a frame with the DP anim codec and bung it into
a comp frame
---------------------------------------------------------------------- */
void DpAnimFilter::Encode(Frame const & Fr,CompFrame & Cfr)
{
Frame NewFrame;
u8 const * Src;
int BytesLeft,Index;
int const px=320;
int const py=200;
int const Size=px*py;
NewFrame=Fr;
NewFrame.Crop(Rect(0,0,px,py));
Src=NewFrame.SeeData();
BytesLeft=px*py;
Cfr.WriteByte('B');
Cfr.WriteByte(0);
Cfr.WriteWord(BMBODY_RUNSKIPDUMP);
while (BytesLeft)
{
int Length;
Index=(px*py)-BytesLeft;
Length=GetRunLength(&Src[Index],BytesLeft);
if (Length > 3)
WriteRun(Src[Index],Length,Cfr);
else
{
Length=GetDataLength(&Src[Index],BytesLeft);
WriteDataRun(&Src[Index],Length,Cfr);
}
BytesLeft-=Length;
}
WriteEnd(Cfr);
}
int DpAnimFilter::GetRunLength(u8 const * Src,int BytesLeft)
{
int Index=0;
int LastIndex;
bool Done=false;
int Previous=Src[0];
for (Index=0;Index<BytesLeft && !Done;Index++)
{
if (Src[Index] != Previous)
Done=true;
else
LastIndex=Index;
Previous=Src[Index];
}
// cout<<"Run of "<<LastIndex+1<<endl;
return(LastIndex+1);
}
int DpAnimFilter::GetDataLength(u8 const * Src,int BytesLeft)
{
int Index=0;
int LastIndex=0;
bool Done=false;
int Previous=Src[0]+1;
int RunLen=0;
for (Index=0;Index<BytesLeft && !Done;Index++)
{
if (Src[Index] == Previous)
{
RunLen++;
}
else
{
LastIndex=Index;
RunLen=0;
}
if (RunLen >= 2)
Done=true;
else
Previous=Src[Index];
}
// cout<<"Data Run of "<<LastIndex+1<<endl;
return(LastIndex+1);
}
void DpAnimFilter::WriteDataRun(u8 const * Data,int Length,CompFrame &Cfr)
{
// cout<<"*** Writing unique of "<<Length<<endl;
while (Length)
{
int ToWrite;
ToWrite=min(MAX_RUN_WRITE,Length);
if (ToWrite<128)
Cfr.WriteByte(ToWrite);
else
{
Cfr.WriteByte(0x80);
Cfr.WriteWord(0x8000|ToWrite);
}
Cfr.WriteData(Data,ToWrite);
Length-=ToWrite;
}
}
void DpAnimFilter::WriteRun(u8 Val,int Length,CompFrame &Cfr)
{
// cout<<"*** Writing run of "<<Length<<endl;
while (Length)
{
int ToWrite;
ToWrite=min(MAX_RUN_WRITE,Length);
if (ToWrite<128)
{
Cfr.WriteByte(0);
Cfr.WriteByte(ToWrite);
}
else
{
Cfr.WriteByte(0x80);
Cfr.WriteWord(0xc000|ToWrite);
}
Cfr.WriteByte(Val);
Length-=ToWrite;
}
}
void DpAnimFilter::WriteEnd(CompFrame &Cfr)
{
Cfr.WriteByte(0x80);
Cfr.WriteWord(0);
}
void DpAnimFilter::PlayRunSkipDump(UBYTE const *src, UBYTE *dst)
{
BOOL Done;
UBYTE * odst;
odst=dst;
Done=FALSE;
while (!Done)
{
u8 count;
count=*src++;
if (count == 0)
{
u8 clearlength,pixel;
clearlength=*src++;
pixel=*src++;
memset(dst,pixel,clearlength);
dst+=clearlength;
}
else
{
if (!(count&0x80))
{
memcpy(dst,src,count);
dst+=count;
src+=count;
}
else
{
count&=~0x80;
if (count==0)
{
uint wordcount;
wordcount=(*src++);
wordcount|=(*src++)<<8;
if (wordcount==0)
Done=TRUE;
else
{
if (wordcount&0x8000)
{
wordcount&=~0x8000;
if(wordcount&0x4000)
{
u8 pixel;
wordcount&=~0x4000;
pixel=*src++;
memset(dst,pixel,wordcount);
}
else
{
memcpy(dst,src,wordcount);
src+=wordcount;
}
}
dst+=wordcount;
}
}
else
dst+=count;
}
}
}
}
/*----------------------------------------------------------------------
DpAnimFIlter Sub Classes
---------------------------------------------------------------------- */
DpAnimFilter::AnimHdr::AnimHdr(void)
{
InitHdr();
}
void DpAnimFilter::AnimHdr::InitHdr(void)
{
id=MakeID('L','P','F',' ');
maxLps=256;
nLps=0; /* updated later */
lRecords=0; /* updated later */
maxRecsPerLp=256; /* updated later */
lpfTableOffset=1280;
contentType=MakeID('A','N','I','M');
width=320;
height=200;
variant=0;
version=0;
hasLastDelta=0;
lastDeltaValid=0;
pixelType=0;
highestBBComp=1;
otherRecordsPerFrame=0;
bitmapRecordsPerFrame=1;
nFrames=0; /* may be updated later */
framesPerSecond=10;
}
DpAnimFilter::ColCycles::ColCycles(void)
{
rate=0;
count=0;
flags=0;
low=0x10;
low=0x1f;
}
int GetMaxRecsPerLp(LpObjVec & LpTable)
{
int MaxLps=0;
int f;
for (f=0;f<LpTable.size();f++)
{
if (LpTable[f].NumOfFrames() > MaxLps)
MaxLps=LpTable[f].NumOfFrames();
}
return(MaxLps);
}
void GenerateLpTable(LpObjVec & LpTable,CompFrameVec & CompFrames)
{
LpObj * ThisObj;
int LpIndex;
int CompIndex;
bool Done;
ThisObj=NULL;
LpIndex=0;
CompIndex=0;
Done=false;
while (!Done)
{
if (CompIndex==CompFrames.size())
Done=TRUE;
else
{
if (!ThisObj)
{
LpTable.resize(LpTable.size()+1);
ThisObj=&LpTable[LpTable.size()-1];
ThisObj->SetBaseRecord(CompIndex);
}
if (ThisObj->AddFrame(CompFrames[CompIndex]))
CompIndex++;
else
ThisObj=NULL;
}
}
}
/*----------------------------------------------------------------------
Anim writing stuff
---------------------------------------------------------------------- */
void DpAnimFilter::MakeAnim(void)
{
}
void DpAnimFilter::WriteHdr(ofstream & Out,AnimHdr & A)
{
Out.write((char const *) &A,sizeof(A));
}
void DpAnimFilter::WriteCycs(ofstream & Out)
{
ColCycles MyCycles[MAX_NUMBER_CYCLES];
Out.write((char const *) MyCycles,sizeof(MyCycles));
}
void DpAnimFilter::WritePal(ofstream & Out,Palette const & P)
{
for (int f=0;f<256;f++)
{
Out.put((u8)P[f].GetB());
Out.put((u8)P[f].GetG());
Out.put((u8)P[f].GetR());
Out.put((u8)0);
}
}
void DpAnimFilter::WriteLpTable(ofstream & Out,LpObjVec & LpTable)
{
int WriteFromTable;
int WriteDummy;
int Val=Out.tellp();
WriteFromTable=min(MAX_LARGE_PAGE,LpTable.size());
for (int f=0;f<WriteFromTable;f++)
LpTable[f].WriteHdr(Out);
WriteDummy=MAX_LARGE_PAGE-WriteFromTable;
if (WriteDummy)
{
LpObj Dummy;
for (int f=0;f<WriteDummy;f++)
Dummy.WriteHdr(Out);
}
int NewVal=Out.tellp();
Val=NewVal-Val;
}
void DpAnimFilter::WriteLps(ofstream & Out,LpObjVec & LpTable)
{
for (int f=0;f<LpTable.size();f++)
{
int Written=Out.tellp();
LpTable[f].Write(Out);
int CurrentPos;
CurrentPos=Out.tellp();
Written=CurrentPos-Written;
int ToWrite=LARGE_PAGE_SIZE-Written;
if (ToWrite>0)
{
for (int f=0;f<ToWrite;f++)
Out.put((char)'z');
}
}
}
void DpAnimFilter::DiscardAnim(void)
{
}
/*----------------------------------------------------------------------
A Compressed Frame
---------------------------------------------------------------------- */
CompFrame::CompFrame(void)
{
Init();
Resize(RESIZE_CHUNKS);
}
void CompFrame::Resize(int NewLen)
{
u8 * NewData;
if(!(NewData=new u8[NewLen]))
Error(ERM_OUTOFMEM);
if (BinData)
{
int ToCopy;
ToCopy=min(Len,NewLen);
memcpy(NewData,BinData,ToCopy);
delete BinData;
}
BinData=NewData;
ActualLen=NewLen;
Len=min(Len,ActualLen);
}
void CompFrame::Init(void)
{
Len=0;
ActualLen=0;
BinData=NULL;
}
void CompFrame::Dump(void)
{
if (BinData)
delete BinData;
Init();
}
CompFrame::~CompFrame(void)
{
Dump();
}
void CompFrame::WriteByte(u8 Byte)
{
if ((Len+1) >= ActualLen)
Increase(RESIZE_CHUNKS);
BinData[Len]=Byte;
Len+=1;
}
void CompFrame::WriteWord(u16 Word)
{
if ((Len+2) >= ActualLen)
Increase(RESIZE_CHUNKS);
BinData[Len]=Word&0xff;
BinData[Len+1]=Word>>8;
Len+=2;
}
void CompFrame::WriteData(u8 const * Src,int Amount)
{
if ((Len+Amount) >= ActualLen)
Increase(Amount+RESIZE_CHUNKS);
memcpy(&BinData[Len],Src,Amount);
Len+=Amount;
}
void CompFrame::Write(ofstream & Out) const
{
if (Len)
{
char const * Data;
Data=(char const *)BinData;
Out.write(Data,Len);
}
}
ostream & operator<<(ostream & str,CompFrame & Fr)
{
str<<"size: "<<Fr.GetSize();
return(str);
}
void CompFrame::operator=(CompFrame const & Fr)
{
Dump();
Resize(Fr.Len);
if (Fr.Len)
memcpy(BinData,Fr.BinData,Fr.Len);
Len=Fr.Len;
}
CompFrame::CompFrame(CompFrame const & C)
{
Init();
*this=C;
}
/*----------------------------------------------------------------------
Large Page Shite
---------------------------------------------------------------------- */
LpObj::LpObj(void)
{
BaseRecord=0;
}
void LpObj::WriteHdr(ofstream & Out) const
{
WriteWord(Out,BaseRecord);
WriteWord(Out,MyFrames.size());
WriteWord(Out,GetSizeInAll()+1+1+2);
}
void LpObj::Write(ofstream & Out) const
{
int f;
WriteWord(Out,BaseRecord);
WriteWord(Out,MyFrames.size());
WriteWord(Out,GetSizeInAll()+1+1+2);
WriteWord(Out,0);
for (f=0;f<MyFrames.size();f++)
WriteWord(Out,MyFrames[f].GetSize());
for (f=0;f<MyFrames.size();f++)
{
MyFrames[f].Write(Out);
}
}
bool LpObj::AddFrame(CompFrame const & C )
{
if (((GetSizeInAll()+C.GetSize()) > 64000) && MyFrames.size())
return(false);
else
{
MyFrames.push_back(C);
return(true);
}
}
void LpObj::operator=(LpObj const & No)
{
BaseRecord=No.BaseRecord;
MyFrames=No.MyFrames;
}
int LpObj::GetSizeInAll(void) const
{
int Total=0;
for (int f=0;f<MyFrames.size();f++)
Total+=MyFrames[f].GetSize();
return(Total);
}
/*----------------------------------------------------------------------
Helpy Stuff
---------------------------------------------------------------------- */
void WriteWord(ofstream & Out,u16 Word)
{
Out.put((u8)(Word&0xff));
Out.put((u8)(Word>>8));
}
/*===========================================================================
end */