SBSPSS/Utils/Libs/GLib/Frame.cpp
2000-12-14 16:38:34 +00:00

1681 lines
32 KiB
C++

/*=========================================================================
FRAME.CPP
Author: Gary Liddon @ Fareham
Created:
Project:
Purpose:
Copyright (c) 1997 G R Liddon
===========================================================================*/
/*----------------------------------------------------------------------
Includes
-------- */
/* Std Lib
------- */
#include <conio.h>
#include <math.h>
/* STL
--- */
//#include <alogrithm>
#include <ALGORITHM>
/* Glib
---- */
/* Local
----- */
#include "frame.hpp"
#include "ilbm.hpp"
#include "gutils.h"
#include "misc.hpp"
using namespace std;
static void load_bmp(Frame & frm,char const *filename);
/*----------------------------------------------------------------------
Function: Frame::Frame(void)
---------------------------------------------------------------------- */
Frame::Frame(void)
{
InitFrame();
}
/*----------------------------------------------------------------------
Function: Copy Constructor
---------------------------------------------------------------------- */
Frame::Frame(Frame const & Fr)
{
InitFrame();
CopyFrame(Fr);
}
/*----------------------------------------------------------------------
Function: Destructor
---------------------------------------------------------------------- */
Frame::~Frame(void)
{
if (Buffa)
delete Buffa;
}
/*----------------------------------------------------------------------
Function: Init the Frame
---------------------------------------------------------------------- */
void Frame::InitFrame(void)
{
Buffa=NULL;
X=0;
Y=0;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::CopyFrame(Frame const & Fr)
{
DumpStuff();
Width=Fr.Width;
Height=Fr.Height;
if (Fr.Buffa)
{
if (!(Buffa=new u8[Width*Height]))
Error(ERM_OUTOFMEM);
memcpy(Buffa,Fr.Buffa,Width*Height);
}
else
Buffa=NULL;
Name=Fr.Name;
MyPal=Fr.MyPal;
X=Fr.X;
Y=Fr.Y;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::DumpStuff(void)
{
if (Buffa)
delete Buffa;
Width=0;
Height=0;
X=0;
Y=0;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
bool Frame::IsBlank(u8 BlankVal)
{
if (Buffa)
{
for (int f=0;f<Width*Height;f++)
{
if (Buffa[f] != BlankVal)
return(false);
}
}
return(true);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
bool Frame::Grab(Frame const & Fr,Rect const & RetRect)
{
Rect GrabRect(RetRect);
Rect FromRect=Fr;
FromRect.X=0;
FromRect.Y=0;
FromRect.ClipRect(GrabRect);
if (GrabRect)
{
InitFrame();
u8 const * GrAddr=&Fr.Buffa[GrabRect.Y*Fr.Width+GrabRect.X];
if (Buffa=new u8[GrabRect.W*GrabRect.H])
{
Width=GrabRect.W;
Height=GrabRect.H;
MyPal=Fr.MyPal;
for (int y=0;y<Height;y++)
memcpy(&Buffa[y*Width],&GrAddr[y*Fr.Width],Width);
}
else
Error(ERM_OUTOFMEM);
return(true);
}
return(false);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
bool Frame::DrawBox(Rect const & RetRect,u8 Col)
{
Rect GrabRect(RetRect);
Rect FromRect=*this;
FromRect.X=0;
FromRect.Y=0;
FromRect.ClipRect(GrabRect);
if (GrabRect)
{
u8 * GrAddr=&Buffa[GrabRect.Y*Width+GrabRect.X];
for (int y=0;y<Height;y++)
memset(&GrAddr[y*Width],Col,Width);
return(true);
}
return(false);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::SetFrame(u8 const * nBuffa,int W,int H,Palette const & NewPal)
{
DumpStuff();
Width=W;
Height=H;
MyPal=NewPal;
if (!(Buffa=new u8[Width*Height]))
Error(ERM_OUTOFMEM);
memcpy(Buffa,nBuffa,Width*Height);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::ReduceSinglePixels(int Threshold)
{
UINT cury,curx,col,lastcol,nextcol;
UINT r,g,b,lastr,lastg,lastb,nextr,nextg,nextb,dist1,dist2;
UCHAR * p;
UINT RDLBM256_sy=Height;
UINT RDLBM256_sx=Width;
u8 * inbuf=Buffa;
for(cury=0;cury<RDLBM256_sy;cury++)
{
p = inbuf+(cury*RDLBM256_sx);
for(curx=0;curx<RDLBM256_sx;curx++)
{
col = *p;
nextcol = *(p+1);
if(curx && curx!=RDLBM256_sx-1 && col && col != lastcol && col != nextcol)
{
Colour * C;
Colour * LC;
Colour * NC;
C=&MyPal[col];
LC=&MyPal[lastcol];
NC=&MyPal[nextcol];
r = C->GetR();
g = C->GetG();;
b = C->GetB();;
lastr = LC->GetR();
lastg = LC->GetG();
lastb = LC->GetB();
nextr = NC->GetR();
nextg = NC->GetG();
nextb = NC->GetB();
dist1 = ((lastr-r)*(lastr-r)) + ((lastg-g)*(lastg-g)) + ((lastb-b)*(lastb-b));
dist2 = ((nextr-r)*(nextr-r)) + ((nextg-g)*(nextg-g)) + ((nextb-b)*(nextb-b));
if(dist1 < dist2)
{
if(dist1 <= Threshold)
{
*p = lastcol;
}
}
else
{
if(dist2 <= Threshold)
*p = nextcol;
}
}
lastcol = *p++;
}
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::ReduceSize(void)
{
Rect MRect=FindBoundingRect();
Crop(MRect);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::Crop(Rect const & NewRect)
{
if (Buffa && NewRect)
{
Rect CopyRect;
CopyRect=*this;
CopyRect.X=0;
CopyRect.Y=0;
Rect MyRect=NewRect;
CopyRect.ClipRect(MyRect);
CopyRect=MyRect;
if (CopyRect)
{
u8 * DestBuffa;
if (!(DestBuffa=new u8[NewRect.Area()]))
Error(ERM_OUTOFMEM);
memset(DestBuffa,0,NewRect.Area());
u8 * Src=&Buffa[CopyRect.X+CopyRect.Y*Width];
u8 * Dst=DestBuffa;
for (int yy=0;yy<CopyRect.H;yy++)
{
memcpy(Dst,Src,CopyRect.W);
Src+=Width;
Dst+=NewRect.W;
}
delete Buffa;
Buffa=DestBuffa;
Width=NewRect.W;
Height=NewRect.H;
}
}
}
/*----------------------------------------------------------------------
Function:
Purpose: Resize this image to another size (uses point sampling)
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::Resize(int NewWidth,int NewHeight)
{
if (NewHeight != Height || NewWidth != Width)
{
vector<u8> NewPic;
NewPic.resize(NewWidth*NewHeight);
for (int y=0;y<NewHeight;y++)
for (int x=0;x<NewWidth;x++)
{
float XFrac=float(x)/float(NewWidth);
float YFrac=float(y)/float(NewHeight);
int FromX=float(Width)*XFrac;
int FromY=float(Height)*YFrac;
NewPic[y*NewWidth+x]=Buffa[FromY*Width+FromX];
}
DumpStuff();
Width=NewWidth;
Height=NewHeight;
if(!(Buffa=new u8[Width*Height]))
Error(ERM_OUTOFMEM);
memcpy(Buffa,&NewPic[0],Width*Height);
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
Rect Frame::FindBoundingRect(void)
{
Rect RetRect;
u8 * Bmap;
Bmap=Buffa;
if (Bmap)
{
int Bottom;
int x,y;
Bottom=-1;
for (y=Height-1;y>=0 && Bottom==-1;y--)
{
for (x=0;x<Width && Bottom==-1;x++)
if (Bmap[y*Width+x])
Bottom=y;
}
if (Bottom != -1)
{
int Top=-1;
for (y=0;y<=Bottom && Top==-1;y++)
{
for (x=0;x<Width && Top==-1;x++)
if (Bmap[y*Width+x])
Top=y;
}
if (Top != -1)
{
int Left=-1;
for (x=0;x<Width && Left==-1;x++)
{
for (y=Top;y<=Bottom && Left==-1;y++)
if (Bmap[y*Width+x])
Left=x;
}
if (Left!=-1)
{
int Right=-1;
for (x=Width-1;x>=Left && Right==-1;x--)
{
for (y=Top;y<=Bottom && Right==-1;y++)
if (Bmap[y*Width+x])
Right=x;
}
if (Right !=-1)
{
RetRect.X=Left;
RetRect.Y=Top;
RetRect.W=Right-Left+1;
RetRect.H=Bottom-Top+1;
}
else
Error(ERR_FATAL,"Strange frame");
}
else
Error(ERR_FATAL,"Strange frame");
}
else
Error(ERR_FATAL,"Strange frame");
}
}
return(RetRect);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::SaveLbm(char const * Lbm)
{
u8 * Pal;
Pal=MyPal.MakeDpPal();
nilbm::SavePbm((char *)Lbm,Pal,Buffa,Width,Height);
delete Pal;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::LoadLbm(char const * Lbm)
{
nilbm MyLbm(Lbm);
if (!MyLbm.error())
{
if (Buffa)
delete Buffa;
u8 const * CMap;
Buffa=MyLbm.TakeBmap();
CMap=MyLbm.SeeCmap();
Width=MyLbm.GetWidth();
Height=MyLbm.GetHeight();
int NumOfCols=1<<MyLbm.GetPlanes();
for (int f=0;f<NumOfCols;f++)
{
MyPal[f].SetR(CMap[f*3+0]);
MyPal[f].SetG(CMap[f*3+1]);
MyPal[f].SetB(CMap[f*3+2]);
}
}
else
Error(ERR_FATAL,"Can't load lbm %s",Lbm);
}
/*----------------------------------------------------------------------
Function:
Purpose: Load a paletted BMP file
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::LoadBMP(char const * FileName)
{
load_bmp(*this,FileName);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
int Frame::GetNumOfCols(void) const
{
int Ret;
Ret=0;
if (Buffa)
{
for (int f=0;f<Width*Height;f++)
{
if (Buffa[f] > Ret)
Ret=Buffa[f];
}
}
return(Ret+1);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::FlipX(void)
{
if (Buffa && Width && Height)
{
vector<u8> Buffa2;
Buffa2.resize(Width*Height);
for (int y=0;y<Height;y++)
{
for (int x=0;x<Width;x++)
Buffa2[((Width-1)-x)+y*Width]=Buffa[x+y*Width];
}
memcpy(Buffa,&Buffa2[0],Width*Height);
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::FlipY(void)
{
if (Buffa && Width && Height)
{
vector<u8> Buffa2;
Buffa2.resize(Width*Height);
for (int y=0;y<Height;y++)
memcpy(&Buffa2[((Height-1)-y)*Width],&Buffa[y*Width],Width);
memcpy(Buffa,&Buffa2[0],Width*Height);
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::Remap(Palette const & P)
{
if (Buffa)
{
int * RemapBuffer;
RemapBuffer=P.MakeRemapTable(MyPal);
for (int f=0;f<Width*Height;f++)
Buffa[f]=RemapBuffer[Buffa[f]];
MyPal=P;
delete RemapBuffer;
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::MakeRGBA(u8 * Dest,bool ZeroIsTrans)
{
if (Buffa)
{
int Area;
Area=Width*Height;
for (int f=0;f<Area;f++)
{
Dest[f*4+0]=MyPal[Buffa[f]].GetR();
Dest[f*4+1]=MyPal[Buffa[f]].GetG();
Dest[f*4+2]=MyPal[Buffa[f]].GetB();
if (ZeroIsTrans)
Dest[f*4+3]=Buffa[f] ? 255 : 0;
else
Dest[f*4+3]=255;
}
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::MakeRGB(u8 * Dest)
{
if (Buffa)
{
int Area;
Area=Width*Height;
for (int f=0;f<Area;f++)
{
Dest[f*3+0]=MyPal[Buffa[f]].GetR();
Dest[f*3+1]=MyPal[Buffa[f]].GetG();
Dest[f*3+2]=MyPal[Buffa[f]].GetB();
}
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::Plot(Frame & Dest,int X,int Y)
{
if (Buffa && Dest.Buffa)
{
Rect DestRect=Dest;
Rect NewR(X,Y,Width,Height);
DestRect.X=0;
DestRect.Y=0;
DestRect.ClipRect(NewR);
if (NewR)
{
int NumOfCols=MyPal.GetNumOfCols();
int * RemapBuffer;
int XIndent=NewR.X-X;
int YIndent=NewR.Y-Y;
u8 * PicSrc=&Buffa[XIndent+YIndent*Width];
RemapBuffer=Dest.MyPal.MakeRemapTable(MyPal);
for (int yy=NewR.Y;yy<NewR.Y+NewR.H;yy++)
{
for (int xx=0;xx<NewR.W;xx++)
Dest.Buffa[yy*Dest.Width+NewR.X+xx]=RemapBuffer[PicSrc[(yy-NewR.Y)*Width+xx]];
}
delete RemapBuffer;
}
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::PlotBox(Rect const & R,int Col)
{
Rect NewR(0,0,Width,Height);
Rect DRect;
DRect=R;
NewR.ClipRect(DRect);
if (DRect && Buffa)
{
for (int yy=DRect.Y;yy<DRect.Y+DRect.H;yy++)
memset(&Buffa[yy*Width+DRect.X],Col,DRect.W);
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::PlotTrans(Frame & Dest,int X,int Y,int TrCol)
{
if (Buffa && Dest.Buffa)
{
Rect DestRect=Dest;
Rect NewR(X,Y,Width,Height);
DestRect.X=0;
DestRect.Y=0;
DestRect.ClipRect(NewR);
if (NewR)
{
int NumOfCols=MyPal.GetNumOfCols();
int * RemapBuffer;
int XIndent=NewR.X-X;
int YIndent=NewR.Y-Y;
u8 * PicSrc=&Buffa[XIndent+YIndent*Width];
RemapBuffer=Dest.MyPal.MakeRemapTable(MyPal);
for (int yy=NewR.Y;yy<NewR.Y+NewR.H;yy++)
{
for (int xx=0;xx<NewR.W;xx++)
{
if (PicSrc[(yy-NewR.Y)*Width+xx] != TrCol)
Dest.Buffa[yy*Dest.Width+NewR.X+xx]=RemapBuffer[PicSrc[(yy-NewR.Y)*Width+xx]];
}
}
delete RemapBuffer;
}
}
}
void Frame::ReplaceColour(int ColNum,int RepNum)
{
if (Buffa && Width && Height)
{
if ((ColNum<MyPal.GetNumOfCols()) && (RepNum<MyPal.GetNumOfCols()))
{
for (int f=0;f<Width*Height;f++)
{
if (Buffa[f]==ColNum)
Buffa[f]=RepNum;
}
}
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::ReducePal(void)
{
if (Buffa && Width && Height)
MyPal.SetPalSize(GetNumOfCols());
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::Clear(int Col)
{
if (Buffa && Width && Height)
memset(Buffa,Col,Width*Height);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
struct ColPair
{
Colour C;
int Uses;
bool operator==(ColPair const &Col) const
{return(Uses==Col.Uses);}
bool operator<(ColPair const &Col) const
{return(Uses<Col.Uses);}
};
class PredicateColPair
{
public:
bool operator()(ColPair const & R1,ColPair const & R2) const
{
return (R2.Uses<R1.Uses);
}
};
void Frame::ReduceColours(int TargColours)
{
if (Buffa && TargColours <=255)
{
Palette NewPal;
for (int f=0;f<TargColours;f++)
NewPal[f].SetRGB(0xff,0,0xff);
vector<ColPair> Colours;
Colours.resize(256);
for (f=0;f<256;f++)
Colours[f].Uses=0;
for (f=0;f<Width*Height;f++)
{
if (Buffa[f])
{
Colours[Buffa[f]].C=MyPal[Buffa[f]];
Colours[Buffa[f]].Uses++;
}
}
sort(Colours.begin(),Colours.end());
for (f=63;f>0;f--)
{
int ColIndex=255-63+f;
if (Colours[ColIndex].Uses)
NewPal[f]=Colours[ColIndex].C;
}
Remap(NewPal);
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::AddPixelSurround(int Col)
{
if (Buffa && Col < MyPal.GetNumOfCols())
{
Frame NewFrame;
NewFrame=*this;
NewFrame.Width+=2;
NewFrame.Height+=2;
NewFrame.DumpStuff();
if(!(NewFrame.Buffa=new u8[NewFrame.Width*NewFrame.Height]))
Error(ERM_OUTOFMEM);
memset(NewFrame.Buffa,0,NewFrame.Width*NewFrame.Height);
PlotTrans(NewFrame,0,0);
PlotTrans(NewFrame,0,1);
PlotTrans(NewFrame,0,2);
PlotTrans(NewFrame,1,0);
PlotTrans(NewFrame,1,2);
PlotTrans(NewFrame,2,0);
PlotTrans(NewFrame,2,1);
PlotTrans(NewFrame,2,2);
for (int f=0;f<NewFrame.Width*NewFrame.Height;f++)
{
if (NewFrame.Buffa[f])
NewFrame.Buffa[f]=Col;
}
PlotTrans(NewFrame,1,1);
*this=NewFrame;
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Frame::Expand(int BorderWidth,bool DupeEdges)
{
Frame NewFrame;
NewFrame=*this;
NewFrame.Width+=BorderWidth*2;
NewFrame.Height+=BorderWidth*2;
if(!(NewFrame.Buffa=new u8[NewFrame.Width*NewFrame.Height]))
Error(ERM_OUTOFMEM);
memset(NewFrame.Buffa,0,NewFrame.Width*NewFrame.Height);
if (DupeEdges)
{
for (int f=0;f<BorderWidth*2+1;f++)
Plot(NewFrame,f,BorderWidth);
for (f=0;f<BorderWidth*2+1;f++)
Plot(NewFrame,BorderWidth,f);
}
Plot(NewFrame,BorderWidth,BorderWidth);
*this=NewFrame;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
Rect::Rect(void)
{
X=0;
Y=0;
W=0;
H=0;
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
ostream & operator<<(ostream & str,Rect const & Fr)
{
str<<"X,Y = ("<<Fr.X<<","<<Fr.Y<<") WH=("<<Fr.W<<","<<Fr.H<<")";
return(str);
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
void Rect::ClipRect(Rect & RectToClip) const
{
if (IsColiding(RectToClip))
{
if (RectToClip.X < X)
{
RectToClip.W-=X-RectToClip.X;
RectToClip.X=X;
}
if (RectToClip.Y < Y)
{
RectToClip.H-=Y-RectToClip.Y;
RectToClip.Y=Y;
}
if ((RectToClip.X+RectToClip.W) > (X+W))
RectToClip.W-=(RectToClip.X+RectToClip.W)-(X+W);
if ((RectToClip.Y+RectToClip.H) > (Y+H))
RectToClip.H-=(RectToClip.Y+RectToClip.H)-(Y+H);
}
else
{
RectToClip.W=0;
RectToClip.H=0;
RectToClip.X=0;
RectToClip.Y=0;
}
}
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
bool Rect::IsColiding(Rect const & NewRect) const
{
if ((NewRect.X+NewRect.W) <= X)
return(false);
if ((X+W) <= NewRect.X)
return(false);
if ((NewRect.Y+NewRect.H) <= Y)
return(false);
if ((Y+H) <= NewRect.Y)
return(false);
return(true);
}
struct RGB
{
u8 r,g,b;
};
class BITMAP
{
public:
BITMAP(void)
{
m_width=0;
m_height=0;
}
void setSize(int width,int height)
{
bitMap.resize(width*height);
m_width=width;
m_height=height;
}
void clear(void)
{
if (m_width && m_height)
memset(&bitMap[0],0,m_width*m_height);
}
void line(unsigned int y,unsigned int x,u8 pix)
{
if (x >= m_width || y >= m_height)
GObject::Error(ERR_FATAL,"out of bounds");
else
bitMap[y*m_width+x]=pix;
}
u8 const * getBitMap(void) const
{return(&bitMap[0]);}
protected:
int m_width;
int m_height;
vector<u8> bitMap;
};
/*----------------------------------------------------------------------
Function:
Purpose:
Params:
Returns:
---------------------------------------------------------------------- */
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#define BI_RGB 0
#define BI_RLE8 1
#define BI_RLE4 2
#define BI_BITFIELDS 3
#define OS2INFOHEADERSIZE 12
#define WININFOHEADERSIZE 40
typedef struct BITMAPFILEHEADER
{
unsigned long bfType;
unsigned long bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits;
} BITMAPFILEHEADER;
/* Used for both OS/2 and Windows BMP.
* Contains only the parameters needed to load the image
*/
typedef struct BITMAPINFOHEADER
{
unsigned long biWidth;
unsigned long biHeight;
unsigned short biBitCount;
unsigned long biCompression;
} BITMAPINFOHEADER;
typedef struct WINBMPINFOHEADER /* size: 40 */
{
unsigned long biWidth;
unsigned long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
unsigned long biXPelsPerMeter;
unsigned long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} WINBMPINFOHEADER;
typedef struct OS2BMPINFOHEADER /* size: 12 */
{
unsigned short biWidth;
unsigned short biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
} OS2BMPINFOHEADER;
/* read_bmfileheader:
* Reads a BMP file header and check that it has the BMP magic number.
*/
static int read_bmfileheader(Gifstream & f, BITMAPFILEHEADER *fileheader)
{
fileheader->bfType = f.Get16();
fileheader->bfSize= f.Get32();
fileheader->bfReserved1= f.Get16();
fileheader->bfReserved2= f.Get16();
fileheader->bfOffBits= f.Get32();
if (fileheader->bfType != 19778)
return -1;
return 0;
}
/* read_win_bminfoheader:
* Reads information from a BMP file header.
*/
static int read_win_bminfoheader(Gifstream & f, BITMAPINFOHEADER *infoheader)
{
WINBMPINFOHEADER win_infoheader;
win_infoheader.biWidth = f.Get32();
win_infoheader.biHeight = f.Get32();
win_infoheader.biPlanes = f.Get16();
win_infoheader.biBitCount = f.Get16();
win_infoheader.biCompression = f.Get32();
win_infoheader.biSizeImage = f.Get32();
win_infoheader.biXPelsPerMeter = f.Get32();
win_infoheader.biYPelsPerMeter = f.Get32();
win_infoheader.biClrUsed = f.Get32();
win_infoheader.biClrImportant = f.Get32();
infoheader->biWidth = win_infoheader.biWidth;
infoheader->biHeight = win_infoheader.biHeight;
infoheader->biBitCount = win_infoheader.biBitCount;
infoheader->biCompression = win_infoheader.biCompression;
return 0;
}
/* read_os2_bminfoheader:
* Reads information from an OS/2 format BMP file header.
*/
static int read_os2_bminfoheader(Gifstream & f, BITMAPINFOHEADER *infoheader)
{
OS2BMPINFOHEADER os2_infoheader;
os2_infoheader.biWidth = f.Get16();
os2_infoheader.biHeight = f.Get32();
os2_infoheader.biPlanes = f.Get32();
os2_infoheader.biBitCount = f.Get32();
infoheader->biWidth = os2_infoheader.biWidth;
infoheader->biHeight = os2_infoheader.biHeight;
infoheader->biBitCount = os2_infoheader.biBitCount;
infoheader->biCompression = 0;
return 0;
}
/* read_bmicolors:
* Loads the color pallete for 1,4,8 bit formats.
*/
static void read_bmicolors(int ncols, RGB *pal, Gifstream & f,int win_flag)
{
int i;
for (i=0; i<ncols; i++)
{
pal[i].b = f.get();
pal[i].g = f.get();
pal[i].r = f.get();
if (win_flag)
f.get();
}
}
/* read_1bit_line:
* Support function for reading the 1 bit bitmap file format.
*/
static void read_1bit_line(int length, Gifstream & f, BITMAP *bmp, int line)
{
unsigned char b[32];
unsigned long n;
int i, j, k;
int pix;
for (i=0; i<length; i++)
{
j = i % 32;
if (j == 0)
{
n = f.Get32();
for (k=0; k<32; k++)
{
b[31-k] = n & 1;
n = n >> 1;
}
}
pix = b[j];
bmp->line(line,i,pix);
}
}
/* read_4bit_line:
* Support function for reading the 4 bit bitmap file format.
*/
static void read_4bit_line(int length, Gifstream & f, BITMAP *bmp, int line)
{
unsigned char b[8];
unsigned long n;
int i, j, k;
int temp;
int pix;
for (i=0; i<length; i++)
{
j = i % 8;
if (j == 0)
{
n = f.Get32();
for (k=0; k<4; k++)
{
temp = n & 255;
b[k*2+1] = temp & 15;
temp = temp >> 4;
b[k*2] = temp & 15;
n = n >> 8;
}
}
pix = b[j];
bmp->line(line,i,pix);
}
}
/* read_8bit_line:
* Support function for reading the 8 bit bitmap file format.
*/
static void read_8bit_line(int length, Gifstream & f, BITMAP *bmp, int line)
{
unsigned char b[4];
unsigned long n;
int i, j, k;
int pix;
for (i=0; i<length; i++)
{
j = i % 4;
if (j == 0)
{
n = f.Get32();
for (k=0; k<4; k++)
{
b[k] = n & 255;
n = n >> 8;
}
}
pix = b[j];
bmp->line(line,i,pix);
}
}
/* read_24bit_line:
* Support function for reading the 24 bit bitmap file format, doing
* our best to convert it down to a 256 color pallete.
*/
static void read_24bit_line(int length, Gifstream & f, BITMAP *bmp, int line)
{
#if 0
int i, nbytes;
RGB c;
nbytes=0;
for (i=0; i<length; i++) {
c.b = f.get();
c.g = f.get();
c.r = f.get();
bmp->line[line][i*3+_rgb_r_shift_24/8] = c.r;
bmp->line[line][i*3+_rgb_g_shift_24/8] = c.g;
bmp->line[line][i*3+_rgb_b_shift_24/8] = c.b;
nbytes += 3;
}
nbytes = nbytes % 4;
if (nbytes != 0)
for (i=nbytes; i<4; i++)
f.get();;
#endif
}
/* read_image:
* For reading the noncompressed BMP image format.
*/
static void read_image(Gifstream & f, BITMAP *bmp, BITMAPINFOHEADER *infoheader)
{
int i, line;
for (i=0; i<(int)infoheader->biHeight; i++) {
line = i;
switch (infoheader->biBitCount) {
case 1:
read_1bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1);
break;
case 4:
read_4bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1);
break;
case 8:
read_8bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1);
break;
case 24:
read_24bit_line(infoheader->biWidth, f, bmp, infoheader->biHeight-i-1);
break;
}
}
}
/* read_RLE8_compressed_image:
* For reading the 8 bit RLE compressed BMP image format.
*/
static void read_RLE8_compressed_image(Gifstream & f, BITMAP *bmp, BITMAPINFOHEADER *infoheader)
{
unsigned char count, val, val0;
int j, pos, line;
int eolflag, eopicflag;
eopicflag = 0;
line = infoheader->biHeight - 1;
while (eopicflag == 0) {
pos = 0; /* x position in bitmap */
eolflag = 0; /* end of line flag */
while ((eolflag == 0) && (eopicflag == 0)) {
count = f.get();
val = f.get();
if (count > 0) { /* repeat pixel count times */
for (j=0;j<count;j++) {
bmp->line(line,pos,val);
pos++;
}
}
else {
switch (val) {
case 0: /* end of line flag */
eolflag=1;
break;
case 1: /* end of picture flag */
eopicflag=1;
break;
case 2: /* displace picture */
count = f.get();
val = f.get();
pos += count;
line -= val;
break;
default: /* read in absolute mode */
for (j=0; j<val; j++) {
val0 = f.get();
bmp->line(line,pos,val0);
pos++;
}
if (j%2 == 1)
val0 = f.get(); /* align on word boundary */
break;
}
}
if (pos > (int)infoheader->biWidth)
eolflag=1;
}
line--;
if (line < 0)
eopicflag = 1;
}
}
/* read_RLE4_compressed_image:
* For reading the 4 bit RLE compressed BMP image format.
*/
static void read_RLE4_compressed_image(Gifstream & f, BITMAP *bmp, BITMAPINFOHEADER *infoheader)
{
unsigned char b[8];
unsigned char count;
unsigned short val0, val;
int j, k, pos, line;
int eolflag, eopicflag;
eopicflag = 0; /* end of picture flag */
line = infoheader->biHeight - 1;
while (eopicflag == 0) {
pos = 0;
eolflag = 0; /* end of line flag */
while ((eolflag == 0) && (eopicflag == 0)) {
count = f.get();
val = f.get();
if (count > 0) { /* repeat pixels count times */
b[1] = val & 15;
b[0] = (val >> 4) & 15;
for (j=0; j<count; j++) {
bmp->line(line,pos,b[j%2]);
pos++;
}
}
else {
switch (val) {
case 0: /* end of line */
eolflag=1;
break;
case 1: /* end of picture */
eopicflag=1;
break;
case 2: /* displace image */
count = f.get();
val = f.get();
pos += count;
line -= val;
break;
default: /* read in absolute mode */
for (j=0; j<val; j++) {
if ((j%4) == 0) {
val0 = f.Get16();
for (k=0; k<2; k++) {
b[2*k+1] = val0 & 15;
val0 = val0 >> 4;
b[2*k] = val0 & 15;
val0 = val0 >> 4;
}
}
bmp->line(line,pos,b[j%4]);
pos++;
}
break;
}
}
if (pos > (int)infoheader->biWidth)
eolflag=1;
}
line--;
if (line < 0)
eopicflag = 1;
}
}
/* load_bmp:
* Loads a Windows BMP file, returning a bitmap structure and storing
* the pallete data in the specified pallete (this should be an array of
* at least 256 RGB structures).
*
* Thanks to Seymour Shlien for contributing this function.
*/
static void load_bmp(Frame & frm,char const *filename)
{
BITMAPFILEHEADER fileheader;
BITMAPINFOHEADER infoheader;
RGB pal[256];
BITMAP bmp;
Gifstream f(Gifstream::LITTLE_ENDIAN);
int ncol;
unsigned long biSize;
f.open(filename,ios::in|ios::binary);
if (!f)
GObject::Error(ERR_FATAL,"couldn't open file %s",filename);
if (read_bmfileheader(f, &fileheader) != 0)
{
GObject::Error(ERR_FATAL,"error reading bmp hdr for %s",filename);
}
biSize = f.Get32();
if (biSize == WININFOHEADERSIZE)
{
if (read_win_bminfoheader(f, &infoheader) != 0)
GObject::Error(ERR_FATAL,"error reading windows bmp info hdr for %s",filename);
/* compute number of colors recorded */
ncol = (fileheader.bfOffBits - 54) / 4;
read_bmicolors(ncol, pal, f, 1);
}
else if (biSize == OS2INFOHEADERSIZE)
{
if (read_os2_bminfoheader(f, &infoheader) != 0)
GObject::Error(ERR_FATAL,"error reading os2 bmp info hdr for %s",filename);
/* compute number of colors recorded */
ncol = (fileheader.bfOffBits - 26) / 3;
read_bmicolors(ncol, pal, f, 0);
}
else
{
GObject::Error(ERR_FATAL,"error finding correct hdr for bmp %s",filename);
}
if (infoheader.biBitCount != 4 && infoheader.biBitCount != 8)
GObject::Error(ERR_FATAL,"only handles 4 && 8 bit bmps not %d : %s",infoheader.biBitCount,filename);
bmp.setSize(infoheader.biWidth, infoheader.biHeight);
bmp.clear();
switch (infoheader.biCompression)
{
case BI_RGB:
read_image(f, &bmp, &infoheader);
break;
case BI_RLE8:
read_RLE8_compressed_image(f, &bmp, &infoheader);
break;
case BI_RLE4:
read_RLE4_compressed_image(f, &bmp, &infoheader);
break;
default:
GObject::Error(ERR_FATAL,"unknown compression foramt for %s",filename);
break;
}
f.close();
{
Palette palObj;
for (int f=0;f<ncol;f++)
{
Colour & col = palObj[f];
col.SetRGB(pal[f].r,pal[f].g,pal[f].b);
}
frm.SetFrame(bmp.getBitMap(),infoheader.biWidth,infoheader.biHeight,palObj);
}
}
/*===========================================================================
end */