Copy Link
Add to Bookmark
Report

How to display an image on the screen

Abe's Demoschool: Part III

eZine's profile picture
Published in 
AbeDemoschool
 · 7 Dec 2022

Hello and welcome to part 3 of Abe's Demoschool. Last part was pretty messy so this time I'll show you how to split a demo up into several files and link them all together into one executable file. In this part we'll also cover sprite's and how to show a TGA picture to the screen.

I think it's enough with the TGA format since most drawing programs can convert pictures into the TGA format. The TGA format is also one of the most simple formats.

WHAT IS A SPRITE?

A sprite is a rectangular 2-dimensional picture made up of pixels that can be drawn on different positions on the screen. Games often use sprites.

In a pinball game the ball is a sprite and in DOOM the monsters are sprites.

A car in a car-game viewed from above could consist of 4 sprites. One showing the car facing left, one up, one right and one down. All the computer has to do is to determine where the car is on the screen, which direction it is heading for and then draw the right sprite at that position. To make the car turn smoother it should be made up of at least 8 sprites, facing 8 directions.

To make the sprite routines as fast as possible I used 32-bit instructions (ie movsd) that works with 4 bytes at a time. Because of that, the number of pixels in the x-direction has to be divisible by 4 (ie 4,8,16,20,32,64). The sprites can have any number of pixels in the y-direction.

As usual we are in mode 13h (MCGA) which means there are 256 colors and that every pixel in a sprite is one byte in memory.


This is the way the sprites are stored in memory:
First 2 bytes (one integer) telling the height of the sprite followed by 2 bytes telling the width of the sprite. After that comes all the pixels in the sprites from left to right, top to bottom. Every pixel is one byte.

This is called a raw-format. Some raw formats have the width first but I have the height first because it makes the sprite routines simpler.

EX: A sprite with height 20 pixels and width 16 pixels (16 is divisible by 4)

20, 0,  16, 0           //first the height and width 
//then the raw data
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 Row 1 16 bytes
0,0,0,0,0,34,56,6,6,56,34,0,0,0,0,0 Row 2 16 bytes
.
And So On for 18 more rows.

It is too much work to enter all the sprite-data by hand. You have to use some kind of drawing program to draw the sprites. With the drawing program save the sprite in TGA-format. Be sure to save it in 256-color mode TGA-format.

Then convert it from 256-color-TGA-format to raw-sprite-format with tga2spr which comes with abedemo3.zip. I wrote the tga2spr program a long time ago and it isn't very nice but it works. If you want to make the code faster and clearer, please do, and mail me a copy of the result.


Here are the sprite routines that comes with the source code:

loadsprite( Name, Sprite);                     //loads a sprite from disk 
show32spr( x, y, Sprite, Destination); //Shows a sprite (using fast 32-bit instructions)
//at (x,y) on the Destination
showtrans( x, y, Sprite, Destination); //As show32spr but with color 0 transparent
erase32bit( x, y, Width, Height, Destination); //erases a rectangular area on the Destination
get32spr(x, y, source, sprite, bredd, hojd); //gets a sprite from the source
//Source is either the visible screen or a virtual screen

Loadsprite loads a sprite to Sprite. Sprite is an array that has to be at least as big as the sprite it will contain.

Ex:

char Sprite[2 + MAXWIDTH*MAXHEIGHT]; 
loadsprite("mysprite.spr", Sprite);

show32spr copies the sprite to (x,y) at the destination with the help of the 32-bit instruction movsd

Ex:

screen=0x0a0000000; 
show32spr(10,20, Sprite, screen);

screen=0x0a0000000 makes screen a pointer to the screen (A000:0000), this also gives a warning when you compile the program but who cares, it works (on my compiler). Then Sprite is drawn with the top left corner at (10,20) to the screen.

Showtrans shows a sprite with color 0 transparent. Wherever the sprite has the color 0 the background will shine through the sprite. Showtrans is slower than show32spr, so if the background is black use show32spr instead.

Erase32bit draws a black rectangle with the upper left corner at (x,y) on the Destination with width Width and height Height :-).

It is used to erase a sprite if the background is black. If the background is not black you have to save the background in a temporary background sprite before you draw a sprite to the screen. To erase it, just draw the saved background over the sprite.

Get32spr is used to save the background as described above.

!CAUTION These routines all suppose that the screen is 320 bytes in width.
!CAUTION If you use a virtual screen, it has to be 320 bytes in width.

All the sprite routines are in the file asmdemo3.asm and they contain comments.

HOW CAN I SHOW MORE THAN ONE SPRITE ON THE SCREEN WITH ONLY ONE PALETTE?

This is a problem. If yo also have a picture in the background and several sprites you have to figure out a way for all the sprites to share the same palette.

The easiest way is to draw all the sprites and pictures width the same program and the same palette.

But if you want to use a scanned picture or make sprites in different programs then the sprites and pictures will use one palette each.

Because of this problem I wrote a program that takes one palette and one tga-picture and then transformes the tga-picture to use the closest colors in the palette. I also wrote a program that takes one palette and one sprite with it's palette and transformes the sprite to use the colors in the first palette.

To use the programs first make a palette that contains as many colors as possible of the colors in the sprites and pictures that will use the palette.

Then transform the sprites and pictures with the programs mentioned above (and below).
These programs are called fittga and fitspr and also comes with abedemo3.zip.

They are even worse coded and really needs a facelift.

If anyone does this, please send me a copy. I'm sure shareware programs exists that does the same job much better but I just haven't stumbled over them lately.

If someone should spot such a program on the Internet, mail me the address.

To use fittga and fitspr you have to manually change the filenames in the beginning of the main function. You could change this to make the program take the filenames as parameters to the program instead.

Look at the file tga.txt for more information on the TGA-format.

HOW TO KOMBINE C AND ASSEMBLER

The sprite routines are written in assembler but the main program is written in C. How does it work?

It really is pretty simple. When you compile a C-program to an exe-file you actually do two things. First you compile the C-source to an .obj file, then you link the .obj file to an .exe file. You may not have noticed this if you use an integrated environment like Borland C++ 3.1, which automatically compiles, links and runs the program by a simple keypress (Ctrl F9).

To combine a file written in assembler with a file written in C: assemble the assembly-file, compile the c-file and link the .obj files to an .exe file.

A function written in assembler is called exactly like a function written in C from the C-program. Just tell the compiler that there are functions written outside of the C-file with the command extern.

Ex, suppose the wtsync() function is written in assembler in another file.
Then the C-program will call it like this:

#include <stdio.h>      /* Don't include stdio.h if you don't have to */ 
/* it'll only increase the size of the .exe */
extern void wtsync(void); /* tell the compiler that the wtsync */
/* code is somewhere else */
void main(void)
{
.
.
.
wtsync(); /* wtsync is called the same way as usual */
.
.
.
}

And the assembler file will look like this:

        P386N                ;allow 386-instructions 
IDEAL ;use tasm's ideal mode
MODEL small ;(data < 64k) and (code < 64k)
CODESEG ;The code begins here

PUBLIC _wtsync ;make wtsync public so
;it can be called from the c-program

PROC _wtsync NEAR ;NEAR tells the compiler to put the PROCedure
;in the same codesegment as the code for the
. ;C-program and the other PROCedures.
. ;The PROCedure will only be called with the
. ;offset, not the segment, It's said to be
;NEAR callable
ret ;don't forget the ret or else the
;program will crash
ENDP _wtsync

END

Notice the _ before wtsync in the assemblerfile. You have to add an _ before the function name in the assembler file to make it callable from c.

Don't write the _ in the c-file.

Suppose wtsync is written in the file asmtest.asm and the c-file is ctest.c.
There are a number of ways to build the .exe.

I usually compile the assembler file/files with tasm and use Borland's command line compiler, bcc for the c-source:

With Borland C++ write:

        tasm /ml asmtest 
bcc -c ctest
bcc -ms ctest.obj asmtest.obj

With Turbo C write:

        tasm /ml asmtest 
tcc -c ctest
tcc -ms ctest.obj asmtest.obj

tasm /ml makes the .obj of the assemblerfile. /ml tells tasm to turn on case sensitivity, this is because C uses case sensitivity.

bcc -c makes the .obj of the cfile. -c means only compile, no linking.

bcc -ms links the objectfiles to an executable .exe-file. -ms tells bcc to use memory model small.

The result is a file called ctest.exe because ctest.obj was befor asmtest.obj at the time of the linking.

That's it.

There are lots of advantages writing some of the functions in pure assembler.
One advantage is that we can use 386 specific instructions like movsd. That isn't possible with inline assembler like we used in part 2.

The C-file will also be a lot smaller, which makes it easier to read and understand.

The disadvantage is the writing of the assembler-funktions, that can be hard if you're not used to it (I give you the assembler-functions for free to use and enhance). I have learned to master turbo assembler with the help of the book: mastering turbo assembler by Tom Swan.

IMPORTANT WHEN MIXING ASSEMBLY AND C

* Sending arguments from C to assembler: use the ARG directive (look at the source-code for examples).

Here's the shell of a routine that takes arguments:

                ARG     xpos:word, . . .        ;put all arguments here 
push bp ;this has to do with the stack
mov bp,sp ;don't bother too much about it right now
. ;just put push bp and mov bp,sp first in
. ;the routine and pop bp last
. ;if you're using arguments
.
mov ax,[xpos] ;use the argument (YES IT IS THIS SIMPLE)
. ;remember the braces []
.
.
pop bp
ret

* Sending returnvalues back to the C-program: Return values are sent in ax and dx depending on the size

                SIZE            RETURNREGISTER  EXAMPLE ON DATATYPE 
Byte al char
Word (2 byte) ax int
Dword (4 byte) dx:ax char far*

* Always save ds, si and di on the stack if you change them in the routine

Or else the program will probably crash because C uses ds, si and di.
You are allowed to change AX, BX, CX, DX and ES without saving them.
It doesn't matter for the C-program.


* Avoid local variables in the assembler routines. Use registers instead, they are much faster. If you have to use local directive LOCAL which works like the ARG directive.

You may have noticed that I used ideal mode in the wtsync example. Ideal mode only works with TASM. There are only small differences in IDEAL mode. The differences makes IDEAL mode easier to read and understand. I think of the differences as improvements. Ideal mode is getting pretty big on the internet too, many of the newer assembly-sources you find on internet are in ideal mode.

THE SAMPLEPROGRAM DEMO3.C

All functions in demo3 are written in the assembler file asmdemo3.c
The C-source is in demo3.c. Demo3.c is pretty much explains itself but there are a couple of functions in the assembly-file that needs more explanation.

Showpic reads a picture, stored as raw-data, to the screen or a virtual screen.

If you're using a virtual screen it must be 320 pixels wide.
The program uses a virtual screen the same size as the visible screen 320*200 = 64000 bytes.

First, memory is allocated for the virtual screen using a malloc function which is declared in the asmdemo3.asm. Then the picture is loaded into the virtual screen and a palette is loaded and set.
Then the virtual screen is flipped over to the visible screen using a digital dissolve effect which I will talk more about later.

After the dissolve effect comes the main loop where a transparent sprite bounces across the screen. All the drawing and erasing is made on the virtual screen, when one frame is done the virtual screen is flipped over to the visible screen and the next frame is calculated ...

The main loop follows these steps:

  1. Saves the background where the sprite will be drawn
  2. Draws the sprite
  3. Flipps virt over to the screen
  4. Erase by drawing the saved background over the sprite on the virtual screen
  5. calculate new coordinates for the sprite, GoTo 1

Before the program is finished the allocated memory must be released using the free function which also is declared in asmdemo3.asm.

The Dissolve effect uses an interesting algorithm which gives all numbers between 1 - FFFFh in random order, no number comes up more than once before all numbers have come up !!!

PSEUDOCODE

        set the offset to any number but zero (ie 1) 
for(i=0;i<0x0FFFF;i++)
{
Do something with the offset (like flip one point to the)
. (screen using the offset)
.
.
Shift the offset right 1 bit (like dividing by 2)
if the shifted bit was 1 (if carry is set)
then offset = offset xor 0x0b400
}

This way offset will take on all values between 1 - FFFFh.
Don't ask me how, but it really works.
I got the idea from Graphic Gems I side 221.


See the file targa.txt for a description of the TGA-format.


The next part will be about 2d and 3d vector graphics. I'm currently optimizing the routine that draws a filled triangle to the screen, hopefully it'll get fast. You lucky weezers will get the routines served on a silver plate, but hey, it's internet.


Sprit'em
Says: Abe Raham

electronic mail: dat94avi@bilbo.mdh.se
snail mail:
Albert Veli
Spisringsg. 9
724 76 VÑsterÜs

DISCUSSION

Most computers today have hardware support for drawing sprites (Already the Commodore 64 had it). It's usually called something like bitblt and that means Bit Blast.

On the PC most graphic cards also do have bitblt, but there's no standard so you can't use the BitBlt. Because it'll only work on your own computer and the computers that have similar graphic card to your one.

But if you want to try, there's information about various cards in the book "Programming the Ega, Vga and SuperVga Cards" by Richard Ferraro.

Abe's Demoschool: Part III screenshot
Pin it
Abe's Demoschool: Part III screenshot

TGA.TXT

Explanation of the 256colors TGA-format


Targaformat (pictures that ends with .tga)

Most paint programs support the targaformat, it's also a pretty simple format, that's why I use it.
There isn't much to say, I'll show you the code and comment it.

First in every targafile there is a head that tells which type of targa image it is, width, height and some other data about the picture.

The head can be read into a struct like this:

struct TGAHdrType { 
short int NumOfBytInId;
short int ColorMapType;
short int ImageTypeCode;
unsigned int ColorMapOrigin;
unsigned int ColorMapLength;
short int ColorMapEntrySize;
unsigned int XStart;
unsigned int YStart;
unsigned int Width;
unsigned int Height;
short int PixelSize;
short int ImageDescriptor;
unsigned int PaletteStart;
unsigned int DataStart;
};

This function reads the head from a file with name filename to a struct TGAHdrType (look above). Data is temporary stored in the buffer char TGAHdrData[2048];

void read_TGAHdr(struct TGAHdrType * TGAHdr, char * filename) 
{
FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Can't open the file %s\n",filename);
getch();
exit(1);
}

if (fread(TGAHdrData, 786, 1, fs) != 1)
{
printf("Can't read the TGA-header");
getch();
exit(1);
}

TGAHdr->NumOfBytInId = TGAHdrData[0];
TGAHdr->ColorMapType = TGAHdrData[1];
TGAHdr->ImageTypeCode = TGAHdrData[2];
TGAHdr->ColorMapOrigin = TGAHdrData[3]+256*TGAHdrData[4];
TGAHdr->ColorMapLength = TGAHdrData[5]+256*TGAHdrData[6];
TGAHdr->ColorMapEntrySize = TGAHdrData[7];
TGAHdr->XStart = TGAHdrData[8]+256*TGAHdrData[9];
TGAHdr->YStart = TGAHdrData[10]+256*TGAHdrData[11];
TGAHdr->Width = TGAHdrData[12]+256*TGAHdrData[13];
TGAHdr->Height = TGAHdrData[14]+256*TGAHdrData[15];
TGAHdr->PixelSize = TGAHdrData[16];
TGAHdr->ImageDescriptor = TGAHdrData[17];
TGAHdr->PaletteStart = 18 + TGAHdr->NumOfBytInId;
TGAHdr->DataStart = 18 + TGAHdr->NumOfBytInId + 3 * TGAHdr->ColorMapLength;

fclose(fs);
}

The function below reads the palette from TGAHdrData to the array pal.

void TGAHdr_to_pal_256(struct TGAHdrType TGAHdr,char*pal) 
{
int p; //looprÑknare
int red, green, blue; //r,g och b values vary between 0 - 255
//and in mode 13h r,g,b values
//vary between 0 - 63
//the values heve to be divided by 4

for (p=0; p<256; p++)
{
red = (TGAHdrData[TGAHdr.PaletteStart+2+(p*3)]);
green = (TGAHdrData[TGAHdr.PaletteStart+1+(p*3)]);
blue = (TGAHdrData[TGAHdr.PaletteStart+0+(p*3)]);

pal[p*3]=red>>2; //Divide by four through shifting
pal[p*3+1]=green>>2;
pal[p*3+2]=blue>>2;
}
}

The function below shows an uncompressed tga-image on the screen.

It takes a TGAHdr as parameter, that means the head has to be read before calling this function.

There are 2 types of uncompressed TGA-images, one draws down-up the other draws up-down. This function copes with both

void show_TGA_image_256_uncom(struct TGAHdrType TGAHdr, char * filename, int xpos, int ypos) 
{
int i, j, row; //loopcounter

FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Can't open the file\n");
exit(1);
}

// Sets the filepointer
fseek(fs, TGAHdr.DataStart, SEEK_SET);

if ((TGAHdr.ImageDescriptor&32)==32) {
//draws down - up
row=ypos;
for (i=ypos; i<ypos+TGAHdr.Height; i++)
{
//reads one row
fread(TGAHdrData, TGAHdr.Width, 1, fs);
//Draws the row
for (j=0; j < TGAHdr.Width; j++)
{
putpixel(xpos+j,i,TGAHdrData[j]);
}
}
}
else {
//Draws up-down
row=ypos+TGAHdr.Height;
for (i=row; i>row-TGAHdr.Height; i--)
{
//Reads one row
fread(TGAHdrData, TGAHdr.Width, 1, fs);
//Draw the row
for (j=0; j < TGAHdr.Width; j++)
{
putpixel(xpos+j,i,TGAHdrData[j]);
}
}
}
fclose(fs);
}

This function draws a rlecompressed tga image on the screen.

RLE stands for Run Length Encoding. In an RLE-encoded picture every pixel is not one byte. One byte tells how many pixels in the same color is to follow.

The next byte tells which color it is.

void show_TGA_image_256_rle(struct TGAHdrType TGAHdr, char * filename, int xpos, int ypos) 
{
int row, kol, dir, i;
char pakethdr;
short int pakettyp;
short int hdrnumber;
short int rlecolor;

FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Can't open the file\n");
exit(1);
}

// Set the filepointer
fseek(fs, TGAHdr.DataStart, SEEK_SET);

//Begin in the upper left or lower right corner, check bit 5
if ((TGAHdr.ImageDescriptor&32)==32) {
row=ypos;
dir=1;
}
else {
row=ypos+TGAHdr.Height;
dir=-1;
}

kol=0;

while (!feof(fs)) {
//read pakethdr
fread(&pakethdr, 1, 1, fs);
pakettyp = pakethdr&128;
hdrnumber = pakethdr&127;
if (pakettyp==0) {
//read raw_body
fread(TGAHdrData, hdrnumber+1, 1, fs);
//draw rhe package
for (i=0; i<hdrnumber+1; i++) {
putpixel(xpos+kol,row,TGAHdrData[i]);
kol++;
if ((kol%TGAHdr.Width==0) && (kol!=0)) {
row=row+dir;
kol=0;
}
}
}
else {
//read rle_body
fread(&rlecolor, 1, 1, fs);
//draw
for (i=0; i<hdrnumber+1; i++) {
putpixel(xpos+kol,row,rlecolor);
kol++;
if ((kol%TGAHdr.Width==0) && (kol!=0)) {
row=row+dir;
kol=0;
}
}
}
}
fclose(fs);
}

This function shows a tga-picture on the screen and reads the pictures palette to the array pal. Then you are free to do everything you want with the palette (ie set it);

In fittga the palette is used to compare with another palette which color in it is closest to which pixel in the other palette.

This function only copes with 256colors-tga-images.

If you're using mode13h the picture should be <= 320*200 pixels

void ViewTgaNGetPal(char*image, unsigned char*pal) 
{
struct TGAHdrType TGAHdr;

//Read the tgaheader
read_TGAHdr(&TGAHdr,image);
width=TGAHdr.Width;
height=TGAHdr.Height;

switch (TGAHdr.ImageTypeCode)
{
case 1:
TGAHdr_to_pal_256(TGAHdr,pal);
show_TGA_image_256_uncom(TGAHdr,image,0,0);
break;
case 9:
TGAHdr_to_pal_256(TGAHdr,pal);
show_TGA_image_256_rle(TGAHdr,image,0,0);
break;

default:
printf("\nI can't draw that targa format!!\n");
}
}

This is an example of a mainprogram that uses the functions above

void main(void) 
{
char pal[256*3];
char readname[40]="evildead.tga";

setmode(0x0013);

blackpal(); //set all colors to black so nothing will be shown
//while the picture is drawn
ViewTgaNGetPal(readname,pal); //show the tga and get the palette
setpal(pal); //set the palette and the picture will appear
getch(); //let the user admire the picture until he/she presses a key
setmode(3);
}

I hope that by studying these functions you'll understand how the targa format works.

Abe McCabe
1995-11-16

PS The TGA-routines comes from Dr Dobbs Journal and have been tampered with by me and Thomas Larsson (http://www.mds.mdh.se/~dat93tln)
DS

DEMO3.C

The C-program

 
/************************************************************************/
/* */
/* C-file to part 3 of Abe's Demoschool, picture & sprite */
/* */
/* The program allocates a virtual screen, then a picture is read from */
/* disk to the virtual screen. The picture is flipped over to the */
/* visible screen using a dissolve effekt. */
/* */
/* All drawing is made to the virtual screen, when the drawing is */
/* finished the virtual screen is flipped to the */
/* visible screen and appears. */
/* */
/* The main loop does the following: */
/* */
/* 1 save the background where the sprite will be drawn */
/* */
/* 2 Draw the sprite with color 0 transparent */
/* */
/* 3 Waits for the vertical retrace and flips the virtual screen */
/* over to the visible screen */
/* */
/* 4 Checks if the sprite has hit a wall and calculates new */
/* coordinates (X & Y Values) */
/* */
/* 5 If no button has been pressed, jump to one */
/* */
/* 1996-01-17 */
/* Sir Abe */
/* electronic mail: dat94avi@bilbo.mdh.se */
/* snail mail: Albert Veli */
/* Spisringsg. 9 */
/* 724 76 VÑsterÜs */
/* */
/************************************************************************/


/* char far* means an array that can be outside of the current segment */
/* the program includes no libraries like stdio.h or stdlib.h */
/* they only make the .exe unnecessary big */
/* the code for these functions is in asmdemo3.asm */
/* You could keep these extern-declarations in a separate .h file */
/* I don't because It's enough files already in this part of the demoschool */
extern int loadpalett(char far*name,char far*pal);
extern int loadsprite(char far*name,char far*sprite);
extern int showpic(int x,int y,char far*name,char far*dest);
extern void show32spr(int x,int y,char far*sprite,char far*dest);
extern void showtrans(int x,int y,char far*sprite,char far*dest);
extern void runnyspr(int x,int y,char far*sprite,char far*dest);
extern void erase32bit(int x, int y, int width, int height, char far*dest);
extern void getback32(int x,int y,int width,int height,char far*source,char far*dest);
extern char getch(void);
extern void setmode(int mode);
extern void setpal(char far*pal);
extern void flip64k(char far*source, char far*dest);
extern void disflip64k(char far*source, char far*dest);
extern void disflipblack(char far*source, char far*dest);
extern char far*malloc(unsigned int nr_of_bytes);
extern int free(char far*memoryblock);
extern void wtsync(void);
extern char kbhit(void);


void main(void)
{
char far*virt;
char far*screen;
char pal[3*256],sprite[2+64*64],back[2+64*64];
int x,y,dx,dy,width,height;

screen=0x0a0000000; //this gives a warning but it works (on my compiler :-))

x=120; /* startcoordinates for the sprite */
y=80;
dx=2;dy=1; /* dx & dy = the velocity of the sprite */


virt=malloc(64000); //allocate 64000 bytes for a virtual screen
if(virt!=0) //if it worked, go on
{
erase32bit(0,0,320,200,virt); /* clear virt */
showpic(0,0,"evildead.pic",virt); /* show a picture on virt */
setmode(0x0013); /* enter mode 13h */
loadpalett("palett.col",pal); /* load the palette */
setpal(pal); /* set the palette */
loadsprite("ball.spr",sprite); /* load the sprite */
height=sprite[0] + sprite[1]*256; /* get the dimensions of the sprite */
width=sprite[2] + sprite[3]*256;

runnyspr(x,y,sprite,screen); /* show the sprite with a runny effect */
disflipblack(virt,screen); /* draw the picture with a dissolve effect */
/* the first time (only flips to the black parts of the screen) */
do
{

getback32(x,y,width,height,virt,back); /* save the background */
showtrans(x,y,sprite,virt); /* show the sprite with color 0 transparent */

wtsync(); /* wait for vertical retrace */
flip64k(virt,screen); /* flip virt to screen */

show32spr(x,y,back,virt); /* erase the sprite with the background */


/* if the sprite hits a wall, reverse the direction */
if( (x+dx>319-width) || (x+dx<0)) dx = -dx;
if( (y+dy>199-height) || (y+dy<0)) dy = -dy;
x+=dx; /* update x & y coordinates */
y+=dy;

}while(!kbhit());

erase32bit(0,0,320,200,virt); /* erase virt (make it black) */
disflip64k(virt,screen); /* dissolveflip black virt over to the screen */
setmode(3); /* get back into textmode */
free(virt); /* return the allocated memory for virt */
}
else ; //print an error message here (out of memory or something like that)
//I didn't because I haven't written any text-functions for this part of the demoschool
// There's enough functions as it is now . . .
}


ASMDEMO3.ASM

Contains the assembler's routines that'll be linked with demo3.c

;********************w*h*o**r*u*l*e*s**i*t**a*l*l*?************************* 
;**** ****
;**** To assemble write tasm /ml asmdemo3 ****
;**** C-program is compiled with bcc -c demo3.c ****
;**** link with bcc -ms demo3.obj asmdemo3.obj ****
;**** ( -ms means model small ) ****
;**** ****
;**** To debug with td, build everything with: ****
;**** bcc -v demo3.c asmdemo3.asm ****
;**** Then debug with: td demo3 ****
;**** ****
;**** Albert Veli 96-01-12 ****
;**** mail: dat94avi@bilbo.mdh.se ****
;**** ****
;*********************s*m*a*l*l**s*m*a*l*l**s*m*a*l*l***********************

P386N ;allow 386 instr. (movsd)
IDEAL
MODEL small

DATASEG

foo dw ? ;foo & bar are temporary variables
bar dw ? ;they are used if the registers aint enough

CODESEG
;functions begins with _ then the name
PUBLIC _show32spr ;call with show32spr(x,y,sprite,dest);
PUBLIC _showtrans ; ' ' showtrans(x,y,sprite,dest);
PUBLIC _runnyspr ; ' ' runnyspr(x,y,sprite,dest);
PUBLIC _erase32bit ; ' ' erase32bit(x,y,width,height,dest);
PUBLIC _getback32 ; ' ' getback32(x,y,width,height,source,dest);
PUBLIC _wtsync ; ' ' wtsync();
PUBLIC _setmode ; ' ' setmode(grafikmod);
PUBLIC _blackpal ; ' ' blackpal();
PUBLIC _setpal ; ' ' setpal(palett);
PUBLIC _putpixel ; ' ' putpixel(x,y,color);
PUBLIC _getpixel ; ' ' color=getpixel(x,y);
PUBLIC _getch ; ' ' getch();
PUBLIC _kbhit ; ' ' kbhit();
PUBLIC _loadpalett ; ' ' loadpalett("name", pal);
PUBLIC _loadsprite ; ' ' loadsprite("name", spr);
PUBLIC _showpic ; ' ' showpic("name",x,y);
PUBLIC _flip64k ; ' ' flip64k(source,dest);
PUBLIC _disflip64k ; ' ' disflip64k(source,dest);
PUBLIC _disflipblack ; ' ' disflipblack(source,dest);
PUBLIC _malloc ; ' ' virt=malloc(64000);
PUBLIC _free ; ' ' free(virt);



;************************************************************************
;* void runnyspr(int x,int y,char far*sprite,char far*dest); *
;* Register: ax,bx,cx,dx *
;* width on dest must be 320 bytes *
;* shows sprite with a "runny-effect" *
;************************************************************************
PROC _runnyspr NEAR
ARG xpos:Word, ypos:Word, sprite:Dword, dest:Dword
push bp
mov bp,sp
push ds
push si
push di
lds si,[sprite] ;ds:si = sprite
les di,[dest] ;es:di = destination
mov di,[xpos]
lodsw ;height to ax
mov [foo],ax ;height to foo
cmp ax,0
jz @@error ;if height 0 draw nothing
lodsw ;width to ax
mov cx,ax ;save width in cx
mul [foo] ;calculate size of sprite
sub ax,cx ;minus one row
add si,ax ;ds:si now points to the last row in sprite

mov bx,320 ;bx = pix/screenrow
sub bx,cx ;bx = bytes to next row on the screen
mov ax,cx ;ax = width of sprite

mov [bar],0FFFFh

;draw the sprite with each row falling from the top of the screen down to
;it's final position, beginning with the last row
@@Lp1: mov di,[xpos] ;begin at the top of the screen with each row in sprite
mov dx,[ypos] ;dx = top position of sprite
add dx,[foo] ;dx = final y-position of current row
@@Lp2: dec [bar] ;throw in a pause every 32:nd time
mov cx,[bar]
and cx,31
jnz @@Skip ;skip this row to study the effect in slow motion
call _wtsync ;throw in a pause
@@Skip: mov cx,ax ;cx = number of pixels in one row (width)
rep movsb ;draw one row
sub si,ax ;point si back to the beginning of the row (in sprite)
add di,bx ;di := next row on screen
dec dx
jnz @@Lp2 ;loop until the row reaches it's final
;position on the screen
sub si,ax ;si = row above the one just drawn in sprite
dec [foo]
jnz @@Lp1 ;loop through all rows

@@error:
pop di
pop si
pop ds
pop bp
ret
ENDP _runnyspr



;**************************************************************************
;*** int loadpalett(char far*name,char far*pal); ***
;*** Destroys: ax,bx,cx,dx return:1 OK 0:error ***
;**************************************************************************
PROC _loadpalett NEAR
ARG name:Dword, pal:Dword
push bp
mov bp,sp
push ds
lds dx,[name] ;ds:dx points to the filename
mov ax,3d00h ;open for reading only
int 21h ;open
jc @@errr ;if error jump to errr
mov bx,ax ;bx=filehandle
lds dx,[pal] ;read to pal
mov cx,256*3 ;read the entire palette to pal
mov ah,3fh ;dos read file
int 21h
jc @@errr
mov ah,3eh ;dos close file
int 21h
mov ax,1 ;ax = 1, no error
jmp @@exit
@@errr: xor ax,ax ;ax = 0 error
@@exit: pop ds
pop bp
ret
ENDP _loadpalett

;**************************************************************************
;*** int loadsprite(char far*name,char far*spr); ***
;*** Destroys: ax,bx,cx,dx return:1 OK 0:error ***
;**************************************************************************
PROC _loadsprite NEAR
ARG name:Dword, spr:Dword
push bp
mov bp,sp
push ds
push si
lds dx,[name] ;ds:dx points to the filename
mov ax,3d00h ;open for reading
int 21h ;open
jc @@err ;if error, jump to err
mov bx,ax ;bx=filehandle
lds dx,[spr] ;read to spr
mov cx,4 ;read the head of spr
mov ah,3fh ;dos read file
int 21h
jc @@err
mov si,dx
mov ax,[si] ;height to ax
inc dx
inc dx
mov si,dx
mov cx,[si]
xchg ax,cx
mul cl ;ax=height*width
mov cx,ax
mov ax,3f00h ;dos readfile
inc dx
inc dx
int 21h
jc @@err
mov ah,3eh ;dos close file
int 21h
mov ax,1 ;ax = 1, no error
jmp @@exit
@@err: xor ax,ax ;ax = 0 error
@@exit: pop si
pop ds
pop bp
ret
ENDP _loadsprite


;**************************************************************************
;*** void showpic( int x,int y, char far*name, char far*dest); ***
;*** Destroys: ax,bx,cx,dx return:1 OK 0:error ***
;*** Shows a pic from file on dest (screen or virt) ***
;*** !CAUTION width of dest is assumed to be 320 bytes CAUTION! ***
;**************************************************************************
PROC _showpic NEAR
ARG xpos:Word, ypos:Word, name:Dword, dest:Dword
push bp
mov bp,sp
push ds
push si
push di
les di,[dest]
mov bx,[xpos]
mov ax,[ypos]
xchg ah,al ;ax=256*y
add bx,ax ;bx=256*y+x
shr ax,2 ;ax=64*y
add bx,ax ;bx=320*y+x offset to di
add di,bx ;es:di = destination (screen or virt)
lds dx,[name] ;ds:dx point to filename
mov ax,3d00h ;open for reading
int 21h ;open
jc @@err ;if error, jump to err
mov bx,ax ;bx=filehandle
mov ax,es
mov ds,ax ;ds=segment of destination
mov dx,di ;read to destination
mov cx,4 ;read the head of pic
mov ah,3fh ;dos read file
int 21h
jc @@err
mov si,dx
mov ax,[si] ;height to ax
inc dx
inc dx
mov si,dx
mov cx,[si] ;width to cx
@@lop: shl eax,16 ;save height in high word of eax
mov ax,3f00h ;dos readfile
mov dx,di
int 21h
jc @@err
add di,320
shr eax,16 ;shift height back to ax
dec ax
jnz @@lop
mov ah,3eh ;dos close file
int 21h
mov ax,1 ;ax = 1, no error
jmp @@exit
@@err: xor ax,ax ;ax = 0 error
@@exit: pop di
pop si
pop ds
pop bp
ret
ENDP _showpic


;************************************************************************
;* void putpixel(int x, int y,char color); *
;* Register: ax *
;************************************************************************
PROC _putpixel NEAR
ARG xpos:Word, ypos:Word, color:Byte
push bp
mov bp,sp
push di
mov ax,0A000h
mov es,ax
mov di,[xpos]
mov ax,[ypos]
xchg ah,al ;ax=256*y
add di,ax ;di=256*y+x
shr ax,2 ;ax=64*y
add di,ax ;di=320*y+x offset to di
mov al,[color]
mov [es:di],al
pop di
pop bp
ret
ENDP _putpixel


;************************************************************************
;*** char getpixel(int x, int y); ***
;*** Register: ax return:color ***
;************************************************************************
PROC _getpixel NEAR
ARG xpos:Word, ypos:Word
push bp
mov bp,sp
push di
mov ax,0A000h
mov es,ax
mov di,[xpos]
mov ax,[ypos]
xchg ah,al ;ax=256*y
add di,ax ;di=256*y+x
shr ax,2 ;ax=64*y
add di,ax ;di=320*y+x offset to di
mov al,[es:di] ;color to al
pop di
pop bp
ret
ENDP _getpixel


;************************************************************************
;* void show32spr( int xpos, int ypos, char far*sprite, char far*dest); *
;* Register: ax,bx,cx,dx *
;* width of dest must be 320 bytes *
;* draws sprite at dest at (xpos,ypos) *
;************************************************************************
PROC _show32spr NEAR

ARG xpos:Word, ypos:Word, sprite:Dword, dest:Dword

push bp
mov bp,sp
push ds
push si
push di
lds si,[sprite] ;ds:si = sprite
les di,[dest] ;es:di = destination
mov di,[xpos]
mov ax,[ypos]
xchg ah,al ;ax=256*y
add di,ax ;di=256*y+x
shr ax,2 ;ax=64*y
add di,ax ;di=320*y+x offset to di

;get the proportions from the head
lodsw ;height to ax
mov dx,ax ;height to dx
or ax,ax
jz @@endspr ;if height 0 draw nothing
lodsw ;width to ax
mov bx,320 ;bx = pix/row
sub bx,ax ;bx = incr (# of bytes to next row)
shr ax,2 ;ax = width/4

;draw the sprite
@@Loop32:
mov cx,ax ;# of Dword per row to cx
rep movsd ;move one spriterow from ds:si to es:di
add di,bx ;di:=next row
dec dx
jnz @@Loop32 ;loopa through all rows
@@endspr:
pop di
pop si
pop ds
pop bp
ret
ENDP _show32spr

;************************************************************************
;* void showtrans(int xpos, int ypos, char far*sprite, char far*dest); *
;* Register: ax,bx,cx,dx *
;* width on dest must be 320 bytes *
;* shows sprite with color 0 transparent, slower than show32spr *
;************************************************************************
PROC _showtrans NEAR
ARG xpos:Word, ypos:Word, sprite:Dword, dest:Dword
push bp
mov bp,sp
push ds
push si
push di
lds si,[sprite] ;ds:si = sprite
les di,[dest] ;es:di = destination
mov di,[xpos]
mov ax,[ypos]
xchg ah,al ;ax=256*y
add di,ax ;di=256*y+x
shr ax,2 ;ax=64*y
add di,ax ;di=320*y+x offset to di
lodsw ;height to ax
mov dx,ax ;height to dx
or ax,ax
jz @@error ;if height 0 draw nothing
lodsw ;width to ax
mov bx,320 ;bx = pix/screenrow
sub bx,ax ;bx = incr (bytes to next row)
shl eax,16 ;save width in high word of eax
;draw sprite
@@Lp1: shr eax,16 ;width => ax
mov cx,ax ;bytes/row => cx
shl eax,16 ;save in high word
@@Lp2: mov al,[ds:si] ;load one byte
or al,al
jz @@itsz ;if 0 jump to itsz
mov [es:di],al ;if not 0 draw it
@@itsz: inc di
inc si
loop @@Lp2 ;to next pixel

add di,bx ;di:=next row
dec dx
jnz @@Lp1 ;loop through all rows
@@error:
pop di
pop si
pop ds
pop bp
ret
ENDP _showtrans


;************************************************************************
;* void erase32bit(int x, int y, int width, int height,char far*dest); *
;* Register: eax, ebx, cx, dx, *
;* erases (draws color 0) on a rectangle in dest *
;************************************************************************
PROC _erase32bit NEAR

ARG xpos:Word, ypos:Word, width:Word, height:Word, dest:Dword

push bp
mov bp,sp
push di
les di,[dest]
mov di,[xpos]
mov ax,[ypos]
xchg ah,al ;ax=256*y
add di,ax ;di=256*y+x
shr ax,2 ;ax=64*y
add di,ax ;di=320*y+x
mov ax,[width]
mov dx,[height]
mov bx,320
sub bx,ax
shr ax,2
shl ebx,16
mov bx,ax ;ebx=offset to next row, width in Dword
xor eax,eax ;eax = 0
@@sudda:
mov cx,bx ;Dword/row to cx
rep stosd ;erase one row at es:di
ror ebx,16 ;rotate offset to bx
add di,bx ;di = next row
rol ebx,16 ;rotate back to high word
dec dx
jnz @@sudda ;loopa through all rows
pop di
pop bp
ret
ENDP _erase32bit

;************************************************************************
;* void getback32(int xpos, int ypos, int width, int height, *
;* char far*source, char far*spr); *
;* Register: ax, bx, cx, dx, *
;************************************************************************
; make a sprite out of source at x,y with width width and height height
PROC _getback32 NEAR
ARG xpos:Word, ypos:Word, width:Word, height:Word, source:Dword, sprite:Dword
push bp
mov bp,sp
push di
push si
push ds
lds si,[source] ;ds:si => source
les di,[sprite] ;es:di => sprite
mov si,[xpos]
mov ax,[ypos]
xchg ah,al
add si,ax
shr ax,2
add si,ax ;si=320*y+x
mov ax,[height] ;save height and width
stosw ;first height
mov dx,ax ;dx height counter
mov ax,[width]
stosw ;then width
mov bx,320
sub bx,ax ;bx=bytes to next row
shr ax,2 ;ax = width/4
@@loop: mov cx,ax ;number of Dwords to cx per row
rep movsd ;move one row to sprite
add si,bx ;next row
dec dx ;was it the last?
jnz @@loop ;not, one more
pop ds
pop si
pop di
pop bp
ret
ENDP _getback32

;****************************************
;* void wtsync(void); Reg: inga *
;* waits for vertical retrace *
;****************************************
PROC _wtsync NEAR
push ax
push dx
mov dx,3DAh
@@wt1: in al,dx
test al,8
jne @@wt1
@@wt2: in al,dx
test al,8
je @@wt2
pop dx
pop ax
ret
ENDP _wtsync

;**************************************************
;*** void blackpal(void) REG: al, cx, dx ***
;*** sets all colors to black ***
;**************************************************
PROC _blackpal NEAR
xor al,al
mov dx,3c8h
out dx,al
inc dx
mov cx,256*3
@@lp: out dx,al
loop @@lp
ret
ENDP _blackpal

;************************************************************
;*** void setpal(unsigned char far*pal) REG: none *****
;*** sets the active palette to pal *****
;************************************************************
PROC _setpal NEAR
ARG pal:Dword
push bp
mov bp,sp
push ax
push cx
push dx
push ds
push si
lds si,[pal]
xor al,al
mov dx,3c8h
out dx,al
inc dx
mov cx,256*3
rep outsb
pop si
pop ds
pop dx
pop cx
pop ax
pop bp
ret
ENDP _setpal

;**************************************************************************
;*** char getch(void) Reg:ax ***
;*** waits for keypress, returns the ascii-code of the pressed char ***
;**************************************************************************
PROC _getch NEAR
mov ah,0 ;stops prog & waits for keypress
int 16h ;ascii-code in al, scan-code in ah
ret
ENDP _getch


;*** char kbhit(void) Reg:ax ***
;checks for keypress, no? return 0
;if keypress return the ascii-code of the pressed key
PROC _kbhit NEAR
mov ah,1
int 16h
jnz @@99
xor ax,ax
@@99: ret
ENDP _kbhit


;*** void setmode(int mode) ***
PROC _setmode NEAR
ARG mode:Word
push bp
mov bp,sp
mov ax,[mode]
int 10h
pop bp
ret
ENDP _setmode


;*** void flip64k(char far*source, char far*dest) ***
;*** register: cx ***
;copies 64000 bytes from source to dest
PROC _flip64k NEAR
ARG source:Dword, dest:Dword
push bp
mov bp,sp
push ds
push si
push di
lds si,[source]
les di,[dest]
mov cx,16000 ;64000/4
rep movsd
pop di
pop si
pop ds
pop bp
ret
ENDP _flip64k

;*** void disflip64k(char far*source, char far*dest) ***
;*** register: cx,al,dx ***
;Makes a dissolveflip from source to dist
;Change the speed of the flip by changing the constant 900 (2 places)
PROC _disflip64k NEAR
ARG source:Dword, dest:Dword
push bp
mov bp,sp
push ds
push si
push di
lds si,[source]
les di,[dest]
mov cx,0ffffh
mov di,1
mov dx,900
@@dis1: mov al,[ds:di] ;move 1 point from ds:di to es:di
mov [es:di],al
shr di,1
jnc @@noc ;di is calculated with an algorithm from
xor di,0b400h ;Graphic Gems I (Digital Dissolve effect)
@@noc: dec dx
jnz @@ndel
mov dx,900 ;every 900:th pixel make a delay by
call _wtsync ;calling wtsync
@@ndel: loop @@dis1
pop di
pop si
pop ds
pop bp
ret
ENDP _disflip64k


;*** void disflipblack(char far*source, char far*dest) ***
;*** register: cx,al,dx ***
;Makes a dissolveflip from source to black areas of dist ***
;Change the speed of the flip by changing the constant 900 (2 places)
PROC _disflipblack NEAR
ARG source:Dword, dest:Dword
push bp
mov bp,sp
push ds
push si
push di
lds si,[source]
les di,[dest]
mov cx,0ffffh
mov di,1
mov dx,900
@@dis1: mov al,[es:di] ;investigate the point that shall be flipped
cmp al,0 ;if the screen is not black there
jnz @@skip ;skip it (don't draw the point)
mov al,[ds:di] ;move 1 point from ds:di to es:di
mov [es:di],al
@@skip: shr di,1
jnc @@noc ;di is calculated with an algorithm from
xor di,0b400h ;Graphic Gems I (Digital Dissolve effect)
@@noc: dec dx
jnz @@ndel
mov dx,900 ;every 900:th pixel make a delay by
call _wtsync ;calling wtsync
@@ndel: loop @@dis1
pop di
pop si
pop ds
pop bp
ret
ENDP _disflipblack


;*** char far* malloc(unsigned int bytes) ***
;allocates bytes bytes, returnes far char pointer to memoryblock
;ex: char far* virt;
; virt=malloc(64000);
;if out of memory, return 0
PROC _malloc NEAR
ARG bytes:Word
push bp
mov bp,sp
mov bx,[bytes]
shr bx,4
inc bx
mov ah,48h ;funk 48 allocates ah number of paragraphs
int 21h ;1 paragraph = 16 bytes, segment in ax
mov dx,ax ;move the segment to dx
jnc @@nerr ;if no carry, no error
xor dx,dx ;an error occured . . .
@@nerr: xor ax,ax ;offset always 0
pop bp
ret
ENDP _malloc


;*** int free(char far* block) ***
;frees memory allocated with malloc (routine above)
;send the pointer as parameter ex: free(virt);
PROC _free NEAR
ARG block:Dword
push bp
mov bp,sp
les ax,[block]
mov ah,49h
int 21h
jnc @@nerr
xor ax,ax ;an error occured . . .
@@nerr: pop bp
ret
ENDP _free

END ;Just END no label


BUILD.BAT

Builds the exe-file. Uses tasm och borland c++ 3.1

tasm /ml asmdemo3 
bcc -c demo3.c
bcc -ms demo3.obj asmdemo3.obj

FITTGA.C

Transforms a TGA-picture to a palette and saves it in raw-data-format

/* This file is not a part of the demoschool, it's only a program that */ 
/* I wrote to fit a targaimage to another palette */
/* It probably contains bugs. */
/* It really isn't finished at all, but it works (if not perfect) */
/* Someday I'll rewrite it to work with a real algorithm, this is just */
/* my own guesses of how the colourdistance could be calculated */
/* change the names of the files in the beginning of main if you try to */
/* use this program */



#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>


unsigned char TGAHdrData[2048];
int width;
int height;


struct TGAHdrType {
short int NumOfBytInId;
short int ColorMapType;
short int ImageTypeCode;
unsigned int ColorMapOrigin;
unsigned int ColorMapLength;
short int ColorMapEntrySize;
unsigned int XStart;
unsigned int YStart;
unsigned int Width;
unsigned int Height;
short int PixelSize;
short int ImageDescriptor;
unsigned int PaletteStart;
unsigned int DataStart;

};


void wtsync(void)
{
asm{

mov dx,3DAh
}
WaitVR1:
asm{
in al,dx
test al,8
jne WaitVR1
}
WaitVR2:
asm{
in al,dx
test al,8
je WaitVR2
}
}

void setmode(int mode)
{
asm{
mov ax,mode
int 10h
}
}

void putpixel(int x,int y,unsigned char color)
{
asm{
mov ax,0a000h
mov es,ax
mov ax,y
mov di,ax
xchg ah,al //ax=256*y
shl di,6 //ax=di*y
add di,ax //di=320*y
add di,x
mov al,color
stosb
}
}

unsigned char getpixel(int x,int y)
{
unsigned char color;
asm{
push ds
mov ax,0a000h
mov ds,ax
mov ax,y
mov si,ax
xchg ah,al //ax=256*y
shl si,6 //ax=di*y
add si,ax //si=320*y
add si,x // +x
lodsb
mov color,al
pop ds
}
return(color);
}

//saves the screen from 0,0 width,height, huvud: height,width
void savepic(char*namn)
{
int i,j;
FILE *fs;

if(!(fs = fopen(namn, "wb")))
{
printf("Can't open the file %s\n",namn);
getch();
exit(1);
}

fwrite(&height, 2, 1, fs); //head: hîjd
fwrite(&width, 2, 1, fs); //bredd

for(j=0;j<height;j++) //data
{
for(i=0;i<width;i++) TGAHdrData[i]=getpixel(i,j);
fwrite(TGAHdrData, width, 1, fs);
}
fclose(fs);
}

void loadpic(char*namn)
{
int i,j;
FILE *fs;

if(!(fs = fopen(namn, "rb")))
{
printf("Cant open the file %s\n",namn);
getch();
exit(1);
}

fread(&height, 2, 1, fs); //head: hîjd
fread(&width, 2, 1, fs); //bredd

for(j=0;j<height;j++) //data
{
fread(TGAHdrData, width, 1, fs);
for(i=0;i<width;i++) putpixel(i,j,TGAHdrData[i]);
}
fclose(fs);
}

void read_TGAHdr(struct TGAHdrType * TGAHdr, char * filename)
{
FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Can't open the file %s\n",filename);
getch();
exit(1);
}

if (fread(TGAHdrData, 786, 1, fs) != 1)
{
printf("Can't read the TGA-header");
getch();
exit(1);
}

TGAHdr->NumOfBytInId = TGAHdrData[0];
TGAHdr->ColorMapType = TGAHdrData[1];
TGAHdr->ImageTypeCode = TGAHdrData[2];
TGAHdr->ColorMapOrigin = TGAHdrData[3]+256*TGAHdrData[4];
TGAHdr->ColorMapLength = TGAHdrData[5]+256*TGAHdrData[6];
TGAHdr->ColorMapEntrySize = TGAHdrData[7];
TGAHdr->XStart = TGAHdrData[8]+256*TGAHdrData[9];
TGAHdr->YStart = TGAHdrData[10]+256*TGAHdrData[11];
TGAHdr->Width = TGAHdrData[12]+256*TGAHdrData[13];
TGAHdr->Height = TGAHdrData[14]+256*TGAHdrData[15];
TGAHdr->PixelSize = TGAHdrData[16];
TGAHdr->ImageDescriptor = TGAHdrData[17];
TGAHdr->PaletteStart = 18 + TGAHdr->NumOfBytInId;
TGAHdr->DataStart = 18 + TGAHdr->NumOfBytInId + 3 * TGAHdr->ColorMapLength;

fclose(fs);
}


void TGAHdr_to_pal_256(struct TGAHdrType TGAHdr,unsigned char*pal)
{
int p; //loopcounter
int red, green, blue; //to set the palett

for (p=0; p<256; p++)
{
red = (TGAHdrData[TGAHdr.PaletteStart+2+(p*3)]);
green = (TGAHdrData[TGAHdr.PaletteStart+1+(p*3)]);
blue = (TGAHdrData[TGAHdr.PaletteStart+0+(p*3)]);

pal[p*3]=red>>2; //red/4
pal[p*3+1]=green>>2;
pal[p*3+2]=blue>>2;
}
}



void show_TGA_image_256_uncom(struct TGAHdrType TGAHdr, char * filename, int xpos, int ypos)
{
int i, j, rad; //looprÑknare

FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Can't open the file\n");
exit(1);
}

// SÑtter filpekaren
fseek(fs, TGAHdr.DataStart, SEEK_SET);

if ((TGAHdr.ImageDescriptor&32)==32) {
//ritar uppifrÜn och nedÜt
rad=ypos;
for (i=ypos; i<ypos+TGAHdr.Height; i++)
{
//LÑser in en rad
fread(TGAHdrData, TGAHdr.Width, 1, fs);
//Ritar ut raden
for (j=0; j < TGAHdr.Width; j++)
{
putpixel(xpos+j,i,TGAHdrData[j]);
}
}
}
else {
//ritar nerifrÜn och uppÜt
rad=ypos+TGAHdr.Height;
for (i=rad; i>rad-TGAHdr.Height; i--)
{
//LÑser in en rad
fread(TGAHdrData, TGAHdr.Width, 1, fs);
//Ritar ut raden
for (j=0; j < TGAHdr.Width; j++)
{
putpixel(xpos+j,i,TGAHdrData[j]);
}
}
}
fclose(fs);
}

void show_TGA_image_256_rle(struct TGAHdrType TGAHdr, char * filename, int xpos, int ypos)
{
int rad, kol, dir, i;
char pakethdr;
short int pakettyp;
short int hdrnumber;
short int rlecolor;

FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("can't open the file\n");
exit(1);
}

// SÑtter filpekaren
fseek(fs, TGAHdr.DataStart, SEEK_SET);

//Bîrja i îvre eller undre vÑnstra hîrnet;Kolla bit 5
if ((TGAHdr.ImageDescriptor&32)==32) {
rad=ypos;
dir=1;
}
else {
rad=ypos+TGAHdr.Height;
dir=-1;
}

kol=0;

while (!feof(fs)) {
//LÑs in pakethuvud
fread(&pakethdr, 1, 1, fs);
pakettyp = pakethdr&128;
hdrnumber = pakethdr&127;
if (pakettyp==0) {
//LÑs in raw_body
fread(TGAHdrData, hdrnumber+1, 1, fs);
//Rita ut paketet
for (i=0; i<hdrnumber+1; i++) {
putpixel(xpos+kol,rad,TGAHdrData[i]);
kol++;
if ((kol%TGAHdr.Width==0) && (kol!=0)) {
rad=rad+dir;
kol=0;
}
}
}
else {
//LÑs in rle_body
fread(&rlecolor, 1, 1, fs);
//Rita ut paketet
for (i=0; i<hdrnumber+1; i++) {
putpixel(xpos+kol,rad,rlecolor);
kol++;
if ((kol%TGAHdr.Width==0) && (kol!=0)) {
rad=rad+dir;
kol=0;
}
}
}
}
fclose(fs);
}


void ViewTgaNGetPal(char*image, unsigned char*pal)
{
struct TGAHdrType TGAHdr;

//LÑser TGA-bildens header
read_TGAHdr(&TGAHdr,image);
width=TGAHdr.Width;
height=TGAHdr.Height;

switch (TGAHdr.ImageTypeCode)
{
case 1:
TGAHdr_to_pal_256(TGAHdr,pal);
show_TGA_image_256_uncom(TGAHdr,image,0,0);
break;
case 9:
TGAHdr_to_pal_256(TGAHdr,pal);
show_TGA_image_256_rle(TGAHdr,image,0,0);
break;

default:
printf("\nDet targaformatet klarar jag inte av!!\n");
}
}



void loadpalett(char*filename,unsigned char*pal)
{
int i;
unsigned char palett[256*3];
FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Kan ej îppna filen %s\n",filename);
getch();
exit(1);
}

fread(palett, 256*3, 1, fs);
fclose(fs);

for(i=0;i<256*3;i++) pal[i]=palett[i];
}


void setpal(unsigned char*palett)
{
int i;

outp(0x3c8,0);
for(i=0;i<256*3;i++)
outp(0x3c9,palett[i]);
}

void showpal(void)
{
int i,j,h,b,ypos;
b=5;
h=4;
ypos=199-16*h;
for(i=0;i<16*b;i++)
{
for(j=0;j<16*h;j++)
{
if(i%b!=0 && j%h!=0) putpixel(i,j+ypos,i/b+(j/h)*16);
}
}
}


void rotpal(char*pal,int first, int last)
{
char r,g,b;
int i;

r=pal[first*3 + 0]; //sÑtt r,g,b till fîrsta fÑrgen (fÑrg 0)
g=pal[first*3 + 1];
b=pal[first*3 + 2];

for(i=first*3;i<(last+1)*3;i++) //flytta ner alla fÑrger ett steg
pal[i]=pal[i+3];

pal[last*3+0]=r; //sÑtt sista fÑrgen (255) till r,g,b
pal[last*3+1]=g;
pal[last*3+2]=b;

wtsync();
setpal(pal);
}


void get_TGA_colors(int*colors)
{
int m,n,j;

colors[256]=1; //antal fÑrger hittills i colors[256]
colors[0]=getpixel(0,0);

for(n=1;n<height;n++)
{
for(m=0;m<width;m++)
{
for(j=0;j<colors[256] && colors[j]!=getpixel(m,n);j++);
if(j==colors[256])
{
colors[colors[256]]=getpixel(m,n);
colors[256]++;
}
}
}
}


void extga(int*ex)
{
int m,n,i,j,flag;


for(n=0;n<height;n++)
{
for(m=0;m<width;m++)
{
i=0;
flag=0;
do
{
if(getpixel(m,n)==ex[2*i])
{
putpixel(m,n,ex[2*i+1]);
flag=1;
}
i++;
}while(ex[2*i]!=-1 && flag==0);

}
}
}

float passdiff(int r1,int g1,int b1,int color, unsigned char*thepal)
{
int r2,g2,b2;

r2=thepal[color*3+0];
g2=thepal[color*3+1];
b2=thepal[color*3+2];

return ((float)(sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2))));
}

int getclosest(int color,unsigned char*pal,unsigned char*thepal,int min,int max)
{
int closest,i,r1,g1,b1;
float value,temp;

value=10000;

r1=pal[color*3+0];
g1=pal[color*3+1];
b1=pal[color*3+2];

for(i=min;i<=max;i++)
{
temp=passdiff(r1,g1,b1,i,thepal);
if(temp<value)
{
value=temp;
closest=i;
}
}
return(closest);
}

void calcpassex(int*ex,int*colors,unsigned char*pal,unsigned char*thepal,int min,int max)
{
int i;

for(i=0;i<256*2;i++) ex[i]=-1;

for(i=0;i<colors[256];i++)
{
ex[2*i+0]=colors[i];
ex[2*i+1]=getclosest(colors[i],pal,thepal,min,max);
}
}

void fittgapal(unsigned char*pal,unsigned char*thepal,int min,int max)
{
int colors[257],ex[256*2];

get_TGA_colors(colors);
calcpassex(ex,colors,pal,thepal,min,max);
extga(ex);
}

void main(void)
{
unsigned char pal[256*3],thepal[256*3];
unsigned char readnamn[40]="c:\\pics\\evildead.tga"; //Change this input name
unsigned char writenamn[40]="evildead.pic"; // and this, the output

setmode(0x0013);
loadpalett("pal2.pal",thepal); //this is the palette the tga should be adjusted to fit

ViewTgaNGetPal(readnamn,pal);
// showpal();
setpal(pal);
getch();

fittgapal(pal,thepal,0,255);
showpal();
setpal(thepal);
getch();

savepic(writenamn);

setmode(3);
}


FITSPR.C

Fits a sprite with palette to another palette

/* Change the names in the beginning of main to your own sprite and 
palette names if you try to use this program.

This is not a part of the demoschool, only a helpprogram that probably
doesn't work perfectly.
The comment's doesn't exist or are in swedish
*/



#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>


unsigned char TGAHdrData[2048];
int width;
int height;


struct TGAHdrType {
short int NumOfBytInId;
short int ColorMapType;
short int ImageTypeCode;
unsigned int ColorMapOrigin;
unsigned int ColorMapLength;
short int ColorMapEntrySize;
unsigned int XStart;
unsigned int YStart;
unsigned int Width;
unsigned int Height;
short int PixelSize;
short int ImageDescriptor;
unsigned int PaletteStart;
unsigned int DataStart;

};


void wtsync(void)
{
asm{

mov dx,3DAh
}
WaitVR1:
asm{
in al,dx
test al,8
jne WaitVR1
}
WaitVR2:
asm{
in al,dx
test al,8
je WaitVR2
}
}

void setmode(int mode)
{
asm{
mov ax,mode
int 10h
}
}

void putpixel(int x,int y,unsigned char color)
{
asm{
mov ax,0a000h
mov es,ax
mov ax,y
mov di,ax
xchg ah,al //ax=256*y
shl di,6 //ax=di*y
add di,ax //di=320*y
add di,x
mov al,color
stosb
}
}

unsigned char getpixel(int x,int y)
{
unsigned char color;
asm{
push ds
mov ax,0a000h
mov ds,ax
mov ax,y
mov si,ax
xchg ah,al //ax=256*y
shl si,6 //ax=di*y
add si,ax //si=320*y
add si,x // +x
lodsb
mov color,al
pop ds
}
return(color);
}

//sparar skÑrmen frÜn 0,0 width,height, huvud: height,width
void savepic(char*namn)
{
int i,j;
FILE *fs;

if(!(fs = fopen(namn, "wb")))
{
printf("Kan ej îppna filen %s\n",namn);
getch();
exit(1);
}

fwrite(&height, 2, 1, fs); //hyvud: hîjd
fwrite(&width, 2, 1, fs); //bredd

for(j=0;j<height;j++) //data
{
for(i=0;i<width;i++) TGAHdrData[i]=getpixel(i,j);
fwrite(TGAHdrData, width, 1, fs);
}
fclose(fs);
}

void loadpic(char*namn)
{
int i,j;
FILE *fs;

if(!(fs = fopen(namn, "rb")))
{
printf("Kan ej îppna filen %s\n",namn);
getch();
exit(1);
}

fread(&height, 2, 1, fs); //hyvud: hîjd
fread(&width, 2, 1, fs); //bredd

for(j=0;j<height;j++) //data
{
fread(TGAHdrData, width, 1, fs);
for(i=0;i<width;i++) putpixel(i,j,TGAHdrData[i]);
}
fclose(fs);
}

void read_TGAHdr(struct TGAHdrType * TGAHdr, char * filename)
{
FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Kan ej îppna filen %s\n",filename);
getch();
exit(1);
}

if (fread(TGAHdrData, 786, 1, fs) != 1)
{
printf("Kan ej lÑsa TGA-headern");
getch();
exit(1);
}

TGAHdr->NumOfBytInId = TGAHdrData[0];
TGAHdr->ColorMapType = TGAHdrData[1];
TGAHdr->ImageTypeCode = TGAHdrData[2];
TGAHdr->ColorMapOrigin = TGAHdrData[3]+256*TGAHdrData[4];
TGAHdr->ColorMapLength = TGAHdrData[5]+256*TGAHdrData[6];
TGAHdr->ColorMapEntrySize = TGAHdrData[7];
TGAHdr->XStart = TGAHdrData[8]+256*TGAHdrData[9];
TGAHdr->YStart = TGAHdrData[10]+256*TGAHdrData[11];
TGAHdr->Width = TGAHdrData[12]+256*TGAHdrData[13];
TGAHdr->Height = TGAHdrData[14]+256*TGAHdrData[15];
TGAHdr->PixelSize = TGAHdrData[16];
TGAHdr->ImageDescriptor = TGAHdrData[17];
TGAHdr->PaletteStart = 18 + TGAHdr->NumOfBytInId;
TGAHdr->DataStart = 18 + TGAHdr->NumOfBytInId + 3 * TGAHdr->ColorMapLength;

fclose(fs);
}


void TGAHdr_to_pal_256(struct TGAHdrType TGAHdr,unsigned char*pal)
{
int p; //loopcounter
int red, green, blue; //to set the palett

for (p=0; p<256; p++)
{
red = (TGAHdrData[TGAHdr.PaletteStart+2+(p*3)]);
green = (TGAHdrData[TGAHdr.PaletteStart+1+(p*3)]);
blue = (TGAHdrData[TGAHdr.PaletteStart+0+(p*3)]);

pal[p*3]=red>>2; //red/4
pal[p*3+1]=green>>2;
pal[p*3+2]=blue>>2;
}
}



void show_TGA_image_256_uncom(struct TGAHdrType TGAHdr, char * filename, int xpos, int ypos)
{
int i, j, rad; //looprÑknare

FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Kan ej îppna filen\n");
exit(1);
}

// SÑtter filpekaren
fseek(fs, TGAHdr.DataStart, SEEK_SET);

if ((TGAHdr.ImageDescriptor&32)==32) {
//ritar uppifrÜn och nedÜt
rad=ypos;
for (i=ypos; i<ypos+TGAHdr.Height; i++)
{
//LÑser in en rad
fread(TGAHdrData, TGAHdr.Width, 1, fs);
//Ritar ut raden
for (j=0; j < TGAHdr.Width; j++)
{
putpixel(xpos+j,i,TGAHdrData[j]);
}
}
}
else {
//ritar nerifrÜn och uppÜt
rad=ypos+TGAHdr.Height;
for (i=rad; i>rad-TGAHdr.Height; i--)
{
//LÑser in en rad
fread(TGAHdrData, TGAHdr.Width, 1, fs);
//Ritar ut raden
for (j=0; j < TGAHdr.Width; j++)
{
putpixel(xpos+j,i,TGAHdrData[j]);
}
}
}
fclose(fs);
}

void show_TGA_image_256_rle(struct TGAHdrType TGAHdr, char * filename, int xpos, int ypos)
{
int rad, kol, dir, i;
char pakethdr;
short int pakettyp;
short int hdrnumber;
short int rlecolor;

FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Kan ej îppna filen\n");
exit(1);
}

// SÑtter filpekaren
fseek(fs, TGAHdr.DataStart, SEEK_SET);

//Bîrja i îvre eller undre vÑnstra hîrnet;Kolla bit 5
if ((TGAHdr.ImageDescriptor&32)==32) {
rad=ypos;
dir=1;
}
else {
rad=ypos+TGAHdr.Height;
dir=-1;
}

kol=0;

while (!feof(fs)) {
//LÑs in pakethuvud
fread(&pakethdr, 1, 1, fs);
pakettyp = pakethdr&128;
hdrnumber = pakethdr&127;
if (pakettyp==0) {
//LÑs in raw_body
fread(TGAHdrData, hdrnumber+1, 1, fs);
//Rita ut paketet
for (i=0; i<hdrnumber+1; i++) {
putpixel(xpos+kol,rad,TGAHdrData[i]);
kol++;
if ((kol%TGAHdr.Width==0) && (kol!=0)) {
rad=rad+dir;
kol=0;
}
}
}
else {
//LÑs in rle_body
fread(&rlecolor, 1, 1, fs);
//Rita ut paketet
for (i=0; i<hdrnumber+1; i++) {
putpixel(xpos+kol,rad,rlecolor);
kol++;
if ((kol%TGAHdr.Width==0) && (kol!=0)) {
rad=rad+dir;
kol=0;
}
}
}
}
fclose(fs);
}


void ViewTgaNGetPal(char*image, unsigned char*pal)
{
struct TGAHdrType TGAHdr;

//LÑser TGA-bildens header
read_TGAHdr(&TGAHdr,image);
width=TGAHdr.Width;
height=TGAHdr.Height;

switch (TGAHdr.ImageTypeCode)
{
case 1:
TGAHdr_to_pal_256(TGAHdr,pal);
show_TGA_image_256_uncom(TGAHdr,image,0,0);
break;
case 9:
TGAHdr_to_pal_256(TGAHdr,pal);
show_TGA_image_256_rle(TGAHdr,image,0,0);
break;

default:
printf("\nDet targaformatet klarar jag inte av!!\n");
}
}



void loadpalett(char*filename,unsigned char*pal)
{
int i;
unsigned char palett[256*3];
FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Kan ej îppna filen %s\n",filename);
getch();
exit(1);
}

fread(palett, 256*3, 1, fs);
fclose(fs);

for(i=0;i<256*3;i++) pal[i]=palett[i];
}


void setpal(unsigned char*palett)
{
int i;

outp(0x3c8,0);
for(i=0;i<256*3;i++)
outp(0x3c9,palett[i]);
}

void showpal(void)
{
int i,j,h,b,ypos;
b=5;
h=4;
ypos=199-16*h;
for(i=0;i<16*b;i++)
{
for(j=0;j<16*h;j++)
{
if(i%b!=0 && j%h!=0) putpixel(i,j+ypos,i/b+(j/h)*16);
}
}
}


void rotpal(char*pal,int first, int last)
{
char r,g,b;
int i;

r=pal[first*3 + 0]; //sÑtt r,g,b till fîrsta fÑrgen (fÑrg 0)
g=pal[first*3 + 1];
b=pal[first*3 + 2];

for(i=first*3;i<(last+1)*3;i++) //flytta ner alla fÑrger ett steg
pal[i]=pal[i+3];

pal[last*3+0]=r; //sÑtt sista fÑrgen (255) till r,g,b
pal[last*3+1]=g;
pal[last*3+2]=b;

wtsync();
setpal(pal);
}


void get_TGA_colors(int*colors)
{
int m,n,j;

colors[256]=1; //antal fÑrger hittills i colors[256]
colors[0]=getpixel(0,0);

for(n=1;n<height;n++)
{
for(m=0;m<width;m++)
{
for(j=0;j<colors[256] && colors[j]!=getpixel(m,n);j++);
if(j==colors[256])
{
colors[colors[256]]=getpixel(m,n);
colors[256]++;
}
}
}
}


void extga(int*ex)
{
int m,n,i,j,flag;


for(n=0;n<height;n++)
{
for(m=0;m<width;m++)
{
i=0;
flag=0;
do
{
if(getpixel(m,n)==ex[2*i])
{
putpixel(m,n,ex[2*i+1]);
flag=1;
}
i++;
}while(ex[2*i]!=-1 && flag==0);

}
}
}

float passdiff(int r1,int g1,int b1,int color, unsigned char*thepal)
{
int r2,g2,b2;

r2=thepal[color*3+0];
g2=thepal[color*3+1];
b2=thepal[color*3+2];

return ((float)(sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2))));
}

int getclosest(int color,unsigned char*pal,unsigned char*thepal,int min,int max)
{
int closest,i,r1,g1,b1;
float value,temp;

value=10000;

r1=pal[color*3+0];
g1=pal[color*3+1];
b1=pal[color*3+2];

for(i=min;i<=max;i++)
{
temp=passdiff(r1,g1,b1,i,thepal);
if(temp<value)
{
value=temp;
closest=i;
}
}
return(closest);
}

void calcpassex(int*ex,int*colors,unsigned char*pal,unsigned char*thepal,int min,int max)
{
int i;

for(i=0;i<256*2;i++) ex[i]=-1;

for(i=0;i<colors[256];i++)
{
ex[2*i+0]=colors[i];
ex[2*i+1]=getclosest(colors[i],pal,thepal,min,max);
}
}

void passtgapal(unsigned char*pal,unsigned char*thepal,int min,int max)
{
int colors[257],ex[256*2];

get_TGA_colors(colors);
calcpassex(ex,colors,pal,thepal,min,max);
extga(ex);
}


void viewspr(char*filename,int*bredd,int*hojd)
{
int i,j;
char buffer[320];
FILE *fs;

if (!(fs = fopen(filename, "rb")))
{
printf("Kan ej îppna filen %s\n",filename);
getch();
exit(1);
}

fread(hojd, 2, 1, fs);
fread(bredd, 2, 1, fs);
for(i=0;i<*hojd;i++)
{
fread(buffer, *bredd, 1, fs);
for(j=0;j<*bredd;j++) putpixel(j,i,buffer[j]);
}
fclose(fs);
height=*hojd;
width=*bredd;
}

void savespr(char*filename,int*bredd,int*hojd)
{
int i,j;
char buffer[320];
FILE *fs;

if (!(fs = fopen(filename, "wb")))
{
printf("Kan ej îppna filen %s\n",filename);
getch();
exit(1);
}

fwrite(hojd, 2, 1, fs);
fwrite(bredd, 2, 1, fs);
for(i=0;i<*hojd;i++)
{
for(j=0;j<*bredd;j++) buffer[j]=getpixel(j,i);
fwrite(buffer, *bredd, 1, fs);
}
fclose(fs);

}

void main(void)
{
unsigned char pal[256*3],thepal[256*3];
unsigned char readnamn[40]="ball.spr"; //Input sprite
unsigned char writenamn[40]="balle.spr";//output sprite
int bredd,hojd;


setmode(0x0013);
loadpalett("palett.col",thepal); //the palette the sprite should be adjusted to fit
loadpalett("ball.pal",pal); //the sprite's palette

setpal(pal);
viewspr(readnamn,&bredd,&hojd);

getch();

passtgapal(pal,thepal,0,255);
setpal(thepal);
getch();

savespr(writenamn,&bredd,&hojd);

setmode(3);
}

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT