395 lines
14 KiB
C++
395 lines
14 KiB
C++
#include <STDIO.H>
|
|
#include <STRING.H>
|
|
#include <DOS.H>
|
|
//#include <graphics.h>
|
|
#include "xlib_all.h"
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* ILBM320 by Mark E. Kern. The following source code may be used, */
|
|
/* modified, copied and shared with no obligation to the author */
|
|
/* whatsoever, as long as this notice accompanies the source. */
|
|
/* Please direct any questions to GEnie:MKERN1 or CS:70670,3120. */
|
|
/* Code written in Borland C++ in ANSI C mode. Sept 9,1991 */
|
|
/* ------------------------------------------------------------------------*/
|
|
|
|
|
|
/***************************************************************************\
|
|
** **
|
|
* SHOWLBM by Todd Michael Lewis *
|
|
* *
|
|
* August 27, 1994 *
|
|
* *
|
|
* Using Mark E. Kern's code for the display of ILBMs, this routine was *
|
|
* developed to show ILBMs using the XLIB graphic display library with a *
|
|
* call to showLBM(x, y, LBM_FILE). *
|
|
* *
|
|
** **
|
|
\***************************************************************************/
|
|
|
|
char testPBM[14] =
|
|
{
|
|
3, 1,
|
|
1,1,1,
|
|
1,1,1,
|
|
1,1,0,
|
|
1,0,0,
|
|
};
|
|
|
|
/* This defines the key information header contained in the ILBM format */
|
|
struct headform{
|
|
long flen;
|
|
char msg2[8];
|
|
long hlen;
|
|
int width;
|
|
int length;
|
|
int xoff;
|
|
int yoff;
|
|
char planes;
|
|
char masking;
|
|
char compression;
|
|
char padbyte;
|
|
int transparent;
|
|
char x_aspect;
|
|
char y_aspect;
|
|
int screenWidth;
|
|
int screenHeight;
|
|
};
|
|
|
|
/* Structure used to store RGB values as they are read from the file */
|
|
struct RGBColor {
|
|
char red;
|
|
char green;
|
|
char blue;
|
|
};
|
|
|
|
char RGBpalette[768];
|
|
|
|
int ReadHeader(FILE *picFile, struct headform *header);
|
|
int ReadCMap(FILE *picFile, struct headform *header);
|
|
int DumpToScreen(FILE *picFile, struct headform *header, long page);
|
|
void drawline(int *yoffset,unsigned char Vbuff[322],
|
|
int *scanline,long page,int maxLines);
|
|
long ReverseLong(long num);
|
|
int ReverseWord(int num);
|
|
int expo(int x,int y);
|
|
void setDAC(struct RGBColor pallete[256]);
|
|
|
|
/* expo calculates x^y and returns it as an integer value. */
|
|
int expo(int x,int y){
|
|
int answer=x;
|
|
|
|
while(y > 1){
|
|
answer = answer * x;
|
|
--y;
|
|
}
|
|
return(answer);
|
|
}
|
|
|
|
/* This code reverses the byte order in the long values read in from the
|
|
file. This is to satisfy Intel's byte ordering scheme. */
|
|
long ReverseLong(long num){
|
|
long actualnum;
|
|
|
|
actualnum = ((num >> 24) & 0x000000ff) |
|
|
((num >> 8) & 0x0000ff00) |
|
|
((num << 8) & 0x00ff0000) |
|
|
((num << 24) & 0xff000000);
|
|
return(actualnum);
|
|
}
|
|
|
|
/* This code reverses the byte order in the word values read in from the
|
|
file. This is to satisfy Intel's byte ordering scheme. */
|
|
int ReverseWord(int num){
|
|
int actualnum;
|
|
|
|
actualnum = ((num >> 8) & 0x00ff) |
|
|
((num << 8) & 0xff00);
|
|
return(actualnum);
|
|
}
|
|
|
|
/* main asks for the ILBM file to diplay, opens the file, and then proceeds
|
|
to: 1)Init the graphics.
|
|
2)Read in the major header information.
|
|
3)Read in the color map and set the DAC registers.
|
|
4)Dump the image line by line from the file to the screen.
|
|
5)Wait for a keypress before closing up the file and shutting down. */
|
|
|
|
void showLBM(int startX, int startY, char *filename, long page)
|
|
{
|
|
FILE *picFile;
|
|
struct headform header;
|
|
|
|
picFile = fopen(filename,"rb");
|
|
ReadHeader(picFile,&header);
|
|
ReadCMap(picFile,&header);
|
|
DumpToScreen(picFile,&header, page);
|
|
fclose(picFile);
|
|
}
|
|
|
|
/* ReadHeader takes a file pointer and starts to read in values into the
|
|
header structure previously defined. It first checks to see if the
|
|
file is an ILBM file by looking for the word "FORM", which should be
|
|
in the beginning of the file. */
|
|
int ReadHeader(FILE *picFile, struct headform *header){
|
|
int err;
|
|
char form[5];
|
|
|
|
fread(form,4,1,picFile);
|
|
form[4]='\0';
|
|
if(strcmp("FORM",form) != 0)
|
|
return(1);
|
|
fread(&header->flen,sizeof(header->flen),1,picFile);
|
|
fread(&header->msg2,8,1,picFile);
|
|
fread(&header->hlen,4,1,picFile);
|
|
fread(&header->width,sizeof(header->width),1,picFile);
|
|
fread(&header->length,sizeof(header->length),1,picFile);
|
|
fread(&header->xoff,sizeof(header->xoff),1,picFile);
|
|
fread(&header->yoff,sizeof(header->yoff),1,picFile);
|
|
fread(&header->planes,sizeof(header->planes),1,picFile);
|
|
fread(&header->masking,sizeof(header->masking),1,picFile);
|
|
fread(&header->compression,sizeof(header->compression),1,picFile);
|
|
fread(&header->padbyte,sizeof(header->padbyte),1,picFile);
|
|
fread(&header->transparent,sizeof(header->transparent),1,picFile);
|
|
fread(&header->x_aspect,sizeof(header->x_aspect),1,picFile);
|
|
fread(&header->y_aspect,sizeof(header->y_aspect),1,picFile);
|
|
fread(&header->screenWidth,sizeof(header->screenWidth),1,picFile);
|
|
fread(&header->screenHeight,sizeof(header->screenHeight),1,picFile);
|
|
|
|
/* Finished loading in the header, now reverse the values to make
|
|
them Intel compatible. */
|
|
header->flen = ReverseLong(header->flen);
|
|
header->hlen = ReverseLong(header->hlen);
|
|
header->width = ReverseWord(header->width);
|
|
header->length = ReverseWord(header->length);
|
|
header->xoff = ReverseWord(header->xoff);
|
|
header->yoff = ReverseWord(header->yoff);
|
|
header->transparent = ReverseWord(header->transparent);
|
|
header->screenWidth = ReverseWord(header->screenWidth);
|
|
header->screenHeight = ReverseWord(header->screenHeight);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* ReadCMap first looks for the CMAP text in the file which denotes the
|
|
beginning of the color information. Once it finds the CMAP header, it
|
|
checks to see if the number of colors(x 3 for numbytes) matches the
|
|
length of the CMAP, which is read in after the header. If everything
|
|
is ok, it then loads up the array pallete with the rgb values contained
|
|
in the CMAP section of the file. ReadCMap then calls setDAC to load
|
|
the video DAC registers with the appropriate values. */
|
|
int ReadCMap(FILE *picFile, struct headform *header){
|
|
char form[5];
|
|
long CMAPsize;
|
|
struct palettetype pal;
|
|
unsigned char rgbTuple[3];
|
|
int x;
|
|
int numColors;
|
|
struct RGBColor pallete[256];
|
|
|
|
fread(form,4,1,picFile);
|
|
form[4]='\0';
|
|
if(strcmp("CMAP",form) != 0)
|
|
return(1);
|
|
fread(&CMAPsize,sizeof(CMAPsize),1,picFile);
|
|
CMAPsize=ReverseLong(CMAPsize);
|
|
numColors=expo(2,header->planes);
|
|
if(CMAPsize != (numColors*3))
|
|
return(1);
|
|
for(x=0;x<numColors;++x){
|
|
fread(rgbTuple,3,1,picFile);
|
|
pallete[x].red = rgbTuple[0]>>2;
|
|
pallete[x].green = rgbTuple[1]>>2;
|
|
pallete[x].blue = rgbTuple[2]>>2;
|
|
RGBpalette[x*3] = rgbTuple[0]>>2;
|
|
RGBpalette[x*3+1] = rgbTuple[1]>>2;
|
|
RGBpalette[x*3+2] = rgbTuple[2]>>2;
|
|
}
|
|
setDAC(pallete);
|
|
return(0);
|
|
}
|
|
|
|
/* setDAC uses some pretty sophisticated funtions of Borland C. We first
|
|
set up a bunch of variables to hold CPU register values in type REGS.
|
|
Once we have done this, we can make some low level calls to the video
|
|
BIOS to set up the colors we want in the picture. We enter a loop that
|
|
sets each register to a RGB color combination and calls the BIOS routine
|
|
to set the specific DAC register we are interesed in. */
|
|
void setDAC(struct RGBColor pallete[256])
|
|
{
|
|
union REGS regs;
|
|
int i;
|
|
|
|
/* This sets up each of the 16 pallete entries to enable the proper
|
|
DAC register when the pallete value is combined with the pixel
|
|
value. */
|
|
/* for(i=0;i<16;++i){
|
|
regs.h.ah = 0x10;
|
|
regs.h.al = 0x00;
|
|
regs.h.bh = i;
|
|
regs.h.bl = i;
|
|
int86(0x10,®s,®s);
|
|
}
|
|
|
|
regs.h.bl = 0x00;
|
|
for (i=0;i<256;++i){
|
|
regs.h.ah = 0x10; /* set specific DAC rgb register value */
|
|
// regs.h.al = 0x10; /* subfunction number, set register */
|
|
// regs.h.ch = pallete[i].green; /* CH contains the green value */
|
|
// regs.h.cl = pallete[i].blue; /* CL contains the blue value */
|
|
// regs.h.dh = pallete[i].red; /* DH contains the red value */
|
|
// int86(0x10, ®s, ®s); /* int 10h */
|
|
// ++regs.h.bl;
|
|
// }*/
|
|
x_put_pal_raw(RGBpalette, 256, 0);
|
|
|
|
}
|
|
|
|
/* DumpToScreen unpacks the graphics information contained in the file. It
|
|
first searches for the BODY header which precedes the actual graphics
|
|
data. If we don't find the BODY header, the program will just crash, since
|
|
I don't look out for the end of the file here. Once it finds the header,
|
|
it reads in the next value, which is the length of the body. We use this
|
|
value to read in the the next n number of bytes as specified by the body
|
|
length. ILBM seems to use a run length encoding scheme to pack the data.
|
|
The function looks at the first byte read. If the first byte read in is
|
|
between -1 and -127, then this means we are to read in the next byte
|
|
value, and repeat this value 1 to 127 times depending on the first byte
|
|
value we read. I.E. if the first value we read was -1, we read the next
|
|
value and repeat this value in the scanline 2 times (1-(-1)). If the
|
|
header value was -128, we would do nothing, as this is the no-operation
|
|
code. If the header value, call this n,is between 0 and 128, we interpret
|
|
this to mean the next n bytes are to be read in normally and stuffed
|
|
into the scanline without any sort of expansion or processing. Once this
|
|
function has read enough bytes to make up a scanline (320 bytes in VGA
|
|
mode 13h), we call a routine that dumps the scanline we just built to
|
|
the screen. We keep doing this until we run out of bytes to read. Note
|
|
that it is possible for an image to have more than 200 scanlines of data,
|
|
but our scanline dump routine ignores lines past 200. */
|
|
int DumpToScreen(FILE *picFile, struct headform *header, long page){
|
|
int yoffset=0; /* the y coord of the scanline */
|
|
int index=0; /* index into the scanline array */
|
|
int pixelsToGo=0; /* number of pixels to go to form a line*/
|
|
int i; /* loop counter */
|
|
int maxLines = header->length;
|
|
unsigned char Vbuff[322]; /* buffer to hold the scanline. It
|
|
is 4 bytes longer than the regular
|
|
length so it can hold hsize and
|
|
vsize data for the putimage call */
|
|
int repeat; /* how many times to repeat the byte*/
|
|
char repeatValue; /* raw byte value from file */
|
|
char bufValue; /* raw buffer value read from file*/
|
|
long size; /* size of the BODY segment */
|
|
long bytesToGo; /* bytes to go till end of BODY */
|
|
char form[5]; /* holds header */
|
|
fread(form,4,1,picFile);
|
|
form[4]='\0';
|
|
while(strcmp("BODY",form) != 0){ /* find the BODY */
|
|
form[0]=form[1];
|
|
form[1]=form[2];
|
|
form[2]=form[3];
|
|
form[3]=fgetc(picFile);
|
|
}
|
|
fread(&size,sizeof(size),1,picFile);
|
|
bytesToGo = ReverseLong(size);
|
|
|
|
// Vbuff[0]=(320-1) & 0xff;
|
|
/* set up height and width of
|
|
our scanline. Since we use
|
|
putimage to dump our scanline,
|
|
we have to tell it how big
|
|
the 'shape' we are drawing
|
|
to the screen is. In our case
|
|
the shape is 320x1 in size.*/
|
|
// Vbuff[1]=(320-1) >> 8;
|
|
// Vbuff[2]=1;
|
|
// Vbuff[3]=0;
|
|
|
|
Vbuff[0] = 160;
|
|
Vbuff[1] = 1;
|
|
|
|
index = index+2;
|
|
|
|
// index = index+4; /* update the index into the scanline */
|
|
|
|
/* Check to see if the compression is of the proper type, which in
|
|
our case is 1. If it is uncompressed, or if we don't know the
|
|
compression type, we exit the program. */
|
|
if(header->compression != 1)
|
|
return(1);
|
|
while(bytesToGo > 0)
|
|
{
|
|
fread(&repeatValue,1,1,picFile);
|
|
if(ferror(picFile))
|
|
{
|
|
}
|
|
--bytesToGo;
|
|
repeat = repeatValue;
|
|
|
|
if (repeat == -128);
|
|
else if((repeat <= -1) && (repeat >= -127))
|
|
{
|
|
fread(&bufValue,1,1,picFile);
|
|
if(ferror(picFile))
|
|
{
|
|
}
|
|
--bytesToGo;
|
|
for (i=0;i<(1-repeat);++i)
|
|
{
|
|
Vbuff[pixelsToGo+4-index] = bufValue;
|
|
++pixelsToGo;
|
|
if(pixelsToGo == 320)
|
|
drawline(&yoffset,Vbuff,&pixelsToGo,page,
|
|
maxLines);
|
|
}
|
|
}
|
|
|
|
else if((repeat >= 0) && (repeat <= 127))
|
|
{
|
|
for(i=0;i<=repeat;++i)
|
|
{
|
|
fread(&(Vbuff[pixelsToGo+4-index]),1,1,picFile);
|
|
if(ferror(picFile))
|
|
{
|
|
}
|
|
--bytesToGo;
|
|
++pixelsToGo;
|
|
if(pixelsToGo == 320)
|
|
drawline(&yoffset,Vbuff,&pixelsToGo,page,maxLines);
|
|
}
|
|
} /*end if*/
|
|
}/*end while bodysize*/
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* drawline takes an array containing the scanline data we have just
|
|
read in, and dumps it to the screen using the putimage call in the
|
|
Borland BGI. The function then increments the yvalue to point to
|
|
the next scanline, then resets the pixels to go value (scanline) to
|
|
0 again. If we are currently working on a scanline greater than can
|
|
fit on the screen (i.e. greater than 200), we just ignore it and don't
|
|
draw it to the screen. */
|
|
void drawline(int *yoffset,unsigned char Vbuff[322],
|
|
int *scanline,long page, int maxLines)
|
|
{
|
|
|
|
// must split image because CHAR cannot hold a length of 320
|
|
|
|
unsigned char PBMbuff[162];
|
|
char *imagePtr;
|
|
|
|
if(*yoffset <= maxLines)
|
|
// putimage(0,*yoffset,Vbuff,COPY_PUT);
|
|
imagePtr = Vbuff;
|
|
x_bm_to_pbm(imagePtr, PBMbuff);
|
|
x_put_pbm(0,*yoffset,page,PBMbuff);
|
|
Vbuff[160] = 160;
|
|
Vbuff[161] = 1;
|
|
imagePtr+=160;
|
|
x_bm_to_pbm(imagePtr, PBMbuff);
|
|
x_put_pbm(160,*yoffset,page,PBMbuff);
|
|
++*yoffset;
|
|
*scanline=0;
|
|
}
|
|
|
|
|