2 Copyright (C) 1998 BJ Eirich (aka vecna)
\r
3 This program is free software; you can redistribute it and/or
\r
4 modify it under the terms of the GNU General Public License
\r
5 as published by the Free Software Foundation; either version 2
\r
6 of the License, or (at your option) any later version.
\r
7 This program is distributed in the hope that it will be useful,
\r
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
\r
10 See the GNU General Public Lic
\r
11 See the GNU General Public License for more details.
\r
12 You should have received a copy of the GNU General Public License
\r
13 along with this program; if not, write to the Free Software
\r
14 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
17 // Mega kudos to aen for porting that GIF code.
\r
22 // ========================= PCX Imaging routines ============================
\r
24 char manufacturer; // pcx header
\r
27 char bits_per_pixel;
\r
34 word bytes_per_line;
\r
41 quad vidoffset, n=0;
\r
46 // ================================= Code ====================================
\r
48 void ReadPCXLine(byte *dest)
\r
53 c=vgetc(pcxf); //*src++;
\r
54 if (c>=0xc0) //(c & 0xc0)==0xc0)
\r
57 c=vgetc(pcxf); //*src++;
\r
60 dest[vidoffset+n++]=c;
\r
62 vseek(pcxf, bytes_per_line-width, 1);
\r
65 void LoadPCXHeader(char *fname)
\r
67 if (!(pcxf=vopen(fname))) err("Could not open PCX file %s.",fname);
\r
68 vread(&manufacturer,1,pcxf);
\r
69 vread(&version,1,pcxf);
\r
70 vread(&encoding,1,pcxf);
\r
71 vread(&bits_per_pixel,1,pcxf);
\r
72 vread(&xmin,2,pcxf);
\r
73 vread(&ymin,2,pcxf);
\r
74 vread(&xmax,2,pcxf);
\r
75 vread(&ymax,2,pcxf);
\r
76 vread(&hres,2,pcxf);
\r
77 vread(&vres,2,pcxf);
\r
78 vread(&palette,48,pcxf);
\r
79 vread(&reserved,1,pcxf);
\r
80 vread(&color_planes,1,pcxf);
\r
81 vread(&bytes_per_line,2,pcxf);
\r
82 vread(&palette_type,2,pcxf);
\r
83 vread(&filler,58,pcxf);
\r
84 //vseek(pcxf,filesize(pcxf)-768,SEEK_SET);
\r
85 //vread(&pal,768,pcxf);
\r
86 vseek(pcxf,128L,SEEK_SET);
\r
90 bytes=bytes_per_line;
\r
92 //for (i=0; i<768; i++)
\r
93 // pal[i]=pal[i] >> 2;
\r
94 // SetPalette((byte *) &pal);
\r
97 //cb=(char *) valloc(i-896, "LoadPCXHeader:cb", OID_TEMP);
\r
98 //vread(cb, i-(128+768+1), pcxf);
\r
101 void LoadPCXHeaderNP(char *fname)
\r
103 if (!(pcxf=vopen(fname))) err("Could not open PCX file %s.",fname);
\r
104 vread(&manufacturer,1,pcxf);
\r
105 vread(&version,1,pcxf);
\r
106 vread(&encoding,1,pcxf);
\r
107 vread(&bits_per_pixel,1,pcxf);
\r
108 vread(&xmin,2,pcxf);
\r
109 vread(&ymin,2,pcxf);
\r
110 vread(&xmax,2,pcxf);
\r
111 vread(&ymax,2,pcxf);
\r
112 vread(&hres,2,pcxf);
\r
113 vread(&vres,2,pcxf);
\r
114 vread(&palette,48,pcxf);
\r
115 vread(&reserved,1,pcxf);
\r
116 vread(&color_planes,1,pcxf);
\r
117 vread(&bytes_per_line,2,pcxf);
\r
118 vread(&palette_type,2,pcxf);
\r
119 vread(&filler,58,pcxf);
\r
120 vseek(pcxf,filesize(pcxf)-768,SEEK_SET);
\r
121 vread(&pal,768,pcxf);
\r
122 vseek(pcxf,128L,SEEK_SET);
\r
125 bytes=bytes_per_line;
\r
128 cb=(char *) valloc(i-896, "LoadPCXHeaderNP:cb", OID_TEMP);
\r
129 vread(cb, i-896, pcxf);
\r
132 void LoadPCX(char *fname, byte *dest)
\r
134 LoadPCXHeader(fname);
\r
137 for (i=0; i<depth; i++)
\r
139 vidoffset=(i*width);
\r
146 byte *LoadPCXBuf(char *fname)
\r
149 // LoadPCXHeaderNP(fname);
\r
150 LoadPCXHeader(fname);
\r
151 buf=(byte *) valloc(width*(depth+1), "LoadPCXBuf:buf", OID_IMAGE);
\r
153 for (vidoffset=0,i=0; i<depth; i++,vidoffset+=width)
\r
156 if (12==vgetc(pcxf))
\r
158 vread(pal,768,pcxf);
\r
159 for (i=0; i<768; i++)
\r
167 // ========================= BMP Imaging routines ============================
\r
171 unsigned char r,g,b,a;
\r
174 struct RGBQUAD bmppalette[256];
\r
176 // ================================= Code ====================================
\r
178 byte *LoadBMPBuf(char *fname)
\r
179 { byte pad[4], *buf;
\r
181 int bwidth, bheight, y;
\r
183 if (!(bmpfile=vopen(fname))) err("Could not open BMP file %s.",fname);
\r
184 vseek(bmpfile, 18, SEEK_SET);
\r
185 vread(&bwidth, 4, bmpfile); width=bwidth;
\r
186 vread(&bheight, 4, bmpfile); depth=bheight;
\r
187 vseek(bmpfile, 54, SEEK_SET);
\r
188 vread(bmppalette, 1024, bmpfile);
\r
189 buf=(byte*) valloc(bwidth*bheight, "LoadBMPBuf:buf", OID_IMAGE);
\r
191 for (y=bheight-1; y>=0; y--)
\r
193 vread((char *)((int)buf+y*width),width,bmpfile);
\r
194 vread(pad,width%4,bmpfile);
\r
196 for (y=0; y<256; y++)
\r
198 pal[y*3]=bmppalette[y].b >> 2;
\r
199 pal[y*3+1]=bmppalette[y].g >> 2;
\r
200 pal[y*3+2]=bmppalette[y].r >> 2;
\r
202 SetPalette((byte*)pal);
\r
207 // ========================== GIF Imaging routines ===========================
\r
209 typedef unsigned char u8;
\r
210 typedef unsigned short u16;
\r
211 typedef unsigned long u32;
\r
213 typedef signed char s8;
\r
214 typedef signed short s16;
\r
215 typedef signed long s32;
\r
229 s16 screenwide, screendeep;
\r
240 } gif_image_descriptor;
\r
242 int NO_CODE = -1,// TRUE = 1,
\r
243 ERROR_EOF = 0, ERROR_BAD_CODE = 1,
\r
244 ERROR_BAD_HEADER = 2, ERROR_BAD_STARTCODE = 3,
\r
245 ERROR_BAD_FIRST_CODE = 4, ERROR_BAD_FILE = 5,
\r
246 ERROR_NO_IMAGE = 6;
\r
248 char* gif_error_messages[] =
\r
250 "Unexpected end of file\n",
\r
252 "Bad gif header\n",
\r
253 "Bad symbol size\n",
\r
254 "Bad first code\n",
\r
255 "Error opening file\n",
\r
256 "This file doesn't contain an image\n"
\r
259 // read colour palette, vga palette values are 6 bit numbers
\r
260 // while gif allows for 8 bit so shift right to get correct colours
\r
262 u8* gif_read_palette(FILE* fp, s32 bytes)
\r
266 s32 components = (bytes / 3) * 3;
\r
268 block = (unsigned char *)valloc(components, "gif_read_palette:block", OID_IMAGE);
\r
270 for (i = 0; i < components; ++i)
\r
271 block[i] = fgetc(fp) >> 2;
\r
276 // read a block of bytes into memory
\r
277 s32 block_mem_read(FILE* fp, u8* buffer, s32 bytes)
\r
281 status = fread(buffer, 1, bytes, fp);
\r
282 if (status != bytes) return EOF;
\r
287 // read a unsigned 16 bit value from file, low byte first; note that this
\r
288 // is reverse endian-ness (ie. fwrite(&s,1,2,fp); writes high byte first).
\r
290 s16 read_word_lbf(FILE* fp)
\r
297 return (b << 8) | a;
\r
300 // read the GIF file header structure into a sequence
\r
301 gif_header* get_gif_header(FILE* fp)
\r
303 gif_header* h = 0L;
\r
305 h = (gif_header *)valloc(sizeof(gif_header), "get_gif_header:h", OID_IMAGE);
\r
307 fread(h->sig, 1, 6, fp);
\r
310 if (strncmp(h->sig, "GIF", 3) != 0)
\r
313 h->screenwide = read_word_lbf(fp); width=h->screenwide;
\r
314 h->screendeep = read_word_lbf(fp); depth=h->screendeep;
\r
315 h->hflags = fgetc(fp);
\r
316 h->background = fgetc(fp);
\r
317 h->aspect = fgetc(fp);
\r
322 // gif file can contain more than one image,
\r
323 // each image is preceeded by a header structure
\r
324 gif_image_descriptor* get_image_descriptor(FILE* fp)
\r
326 gif_image_descriptor* id = 0L;
\r
328 id = (gif_image_descriptor *)valloc(sizeof(gif_image_descriptor), "get_image_descriptor:id", OID_IMAGE);
\r
330 id->left = read_word_lbf(fp);
\r
331 id->top = read_word_lbf(fp);
\r
332 id->wide = read_word_lbf(fp);
\r
333 id->deep = read_word_lbf(fp);
\r
334 id->iflags = fgetc(fp);
\r
339 static u16 word_mask_table[] =
\r
341 0x0000, 0x0001, 0x0003, 0x0007,
\r
342 0x000F, 0x001F, 0x003F, 0x007F,
\r
343 0x00FF, 0x01FF, 0x03FF, 0x07FF,
\r
344 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF
\r
347 static u8 inc_table[] = { 8,8,4,2,0 };
\r
348 static u8 start_table[] = { 0,4,2,1,0 };
\r
350 // enables me to use indices as per EUPHORiA (ie. converts to C's 0 base)
\r
351 #define eui(i) ((i)-1)
\r
353 // unpack an LZW compressed image
\r
354 // returns a sequence containing screen display lines of the image
\r
355 u8* unpack_image(FILE* fp, s32 start_code_size, u32 width, u32 depth, u32 flags)
\r
360 u16 first_code_stack[4096];
\r
361 u16 last_code_stack[4096];
\r
362 u16 code_stack[4096];
\r
379 u8 b[256]; // read buffer; for block reads
\r
380 u8* p; // current byte in read buffer
\r
381 u8* q; // last byte in read buffer + 1
\r
383 line_buffer = (u8 *)valloc(width, "unpack_image:line_buffer", OID_TEMP);
\r
384 buffer = (u8 *)valloc(width * depth, "unpack_image:buffer", OID_IMAGE);
\r
391 if (start_code_size < 2 || start_code_size > 8)
\r
392 err("\nERROR_BAD_STARTCODE"); // bad symbol size
\r
397 clear_code = 1 << start_code_size; //pow(2, start_code_size);
\r
398 next_code = clear_code + 2;
\r
399 code_size = start_code_size + 1;
\r
400 code_size2 = 1 << code_size; //pow(2, code_size);
\r
401 old_code = NO_CODE;
\r
402 old_token = NO_CODE;
\r
406 if (bits_left == 8)
\r
411 block_size = fgetc(fp);
\r
412 if (block_mem_read(fp, b, block_size) == EOF)
\r
413 err("\nERROR_EOF");
\r
415 q = b + block_size;
\r
421 current_code = code_size + bits_left;
\r
423 if (current_code <= 8)
\r
425 *p = *p >> code_size;
\r
426 bits_left = current_code;
\r
433 block_size = fgetc(fp);
\r
434 if (block_mem_read(fp, b, block_size) == EOF)
\r
435 err("\nERROR_EOF");
\r
437 q = b + block_size;
\r
440 this_code |= (*p << (8 - bits_left));
\r
442 if (current_code <= 16)
\r
444 bits_left = current_code - 8;
\r
445 *p = *p >> bits_left;
\r
451 block_size = fgetc(fp);
\r
452 if (block_mem_read(fp, b, block_size) == EOF)
\r
453 err("\nERROR_EOF");
\r
455 q = b + block_size;
\r
458 this_code |= (*p << (16 - bits_left));
\r
460 bits_left = current_code - 16;
\r
461 *p = *p >> bits_left;
\r
465 this_code &= word_mask_table[code_size];
\r
466 current_code = this_code;
\r
468 if (this_code == (clear_code+1) || block_size == 0)
\r
470 if (this_code > next_code)
\r
471 err("\nERROR_BAD_CODE");
\r
473 if (this_code == clear_code)
\r
475 next_code = clear_code + 2;
\r
476 code_size = start_code_size + 1;
\r
477 code_size2 = 1 << code_size; //pow(2, code_size);
\r
478 old_code = NO_CODE;
\r
479 old_token = NO_CODE;
\r
484 if (this_code == next_code)
\r
486 if (old_code == NO_CODE)
\r
487 err("\nERROR_BAD_FIRST_CODE");
\r
489 first_code_stack[eui(u)] = old_token;
\r
491 this_code = old_code;
\r
494 while (this_code >= clear_code)
\r
496 first_code_stack[eui(u)] = last_code_stack[eui(this_code)];
\r
498 this_code = code_stack[eui(this_code)];
\r
501 old_token = this_code;
\r
504 line_buffer[a_byte] = this_code;
\r
506 if (a_byte >= width)
\r
508 // full image line so add it into screen image
\r
509 memcpy(buffer + (line * width), line_buffer, width);
\r
514 line += inc_table[pass];
\r
518 line = start_table[pass];
\r
527 // no more bytes on stack
\r
531 this_code = first_code_stack[eui(u)];
\r
534 if (next_code < 4096 && old_code != NO_CODE)
\r
536 code_stack[eui(next_code)] = old_code;
\r
537 last_code_stack[eui(next_code)] = old_token;
\r
539 if (next_code >= code_size2 && code_size < 12)
\r
542 code_size2 = 1 << code_size; //pow(2, code_size);
\r
546 old_code = current_code;
\r
550 // completed reading the image so return it
\r
555 // skip the extension blocks as we are only after the image
\r
556 void skip_extension(FILE* fp)
\r
561 n = fgetc(fp); // throwaway extension function code
\r
562 n = fgetc(fp); // get length of block
\r
564 while (n > 0 && n != EOF)
\r
567 fread(temp, 1, n, fp);
\r
569 n = fgetc(fp); // get length of next block
\r
573 // unpack the GIF file
\r
574 // returns ImageInfo sequence containing image and image data
\r
575 gif_image_info* unpack_gif(char* filename)
\r
580 gif_header* h = 0L;
\r
581 gif_image_info* ii = 0L;
\r
582 gif_image_descriptor* id = 0L;
\r
583 u8* local_palette = 0L;
\r
585 ii = (gif_image_info *)valloc(sizeof(gif_image_info), "unpack_gif:ii", OID_IMAGE);
\r
587 f = vopen(filename);
\r
588 if (!f) err("Could not open GIF file %s.",filename);
\r
590 if (!fp) err("\nBad filename");
\r
592 // file starts with the Logical Screen Descriptor structure
\r
593 h = get_gif_header(fp);
\r
595 // Size of Global Color Table
\r
596 ii->bits = (h->hflags & 7) + 1;
\r
597 ii->background = h->background;
\r
599 // get Global colour palette if there is one
\r
600 if (h->hflags & 0x80) // is flags bit 8 set?
\r
602 c = 3 << ii->bits; // size of global colour map
\r
603 ii->palette = gif_read_palette(fp, c);
\r
606 // enforce global color map
\r
607 for (s32 i=0; i < 768; ++i)
\r
608 outp(969, ii->palette[i]); */
\r
609 memcpy(pal, ii->palette, 768);
\r
614 while (c == 0x2c || c == 0x21 || c == 0)
\r
616 // image separator so unpack the image
\r
619 id = get_image_descriptor(fp); // get the Image Descriptor
\r
620 // if there is a local Color Table then overwrite the global table
\r
621 if (id->iflags & 0x80)
\r
623 ii->bits = (id->iflags & 7) + 1;
\r
626 free(local_palette);
\r
627 local_palette = gif_read_palette(fp, b);
\r
630 c = fgetc(fp); // get the LZW Minimum Code Size
\r
631 ii->image = unpack_image(fp, c, id->wide, id->deep, id->iflags);
\r
634 // error reading image
\r
636 err("\nerror reading image data");
\r
638 ii->wide = id->wide;
\r
639 ii->deep = id->deep;
\r
641 // return imagedata
\r
644 // extension introducer
\r
645 else if (c == 0x21)
\r
647 skip_extension(fp); // throw the extension away
\r
657 byte *LoadGIFBuf(char *fname)
\r
659 gif_image_info *ii=0;
\r
662 ii=unpack_gif(fname);
\r
670 // ====================== Universal Imaging Interface ========================
\r
674 // ================================= Code ====================================
\r
676 void DetermineFileType(char *fname)
\r
679 if (!strcmp(fname+(strlen(fname)-3),"pcx")) imagetype=0;
\r
680 if (!strcmp(fname+(strlen(fname)-3),"gif")) imagetype=1;
\r
681 if (!strcmp(fname+(strlen(fname)-3),"bmp")) imagetype=2;
\r
684 void VLoadImage(char *fname, byte *dest)
\r
687 DetermineFileType(fname);
\r
690 case 0: LoadPCX(fname, dest); break;
\r
691 case 1: err("VLoadImage() for GIF not supported; use VLoadImageBuf()"); break;
\r
692 case 2: err("VLoadImage() for BMP not supported; use VLoadImageBuf()"); break;
\r
693 default: err("%s: Unrecognized image type.",fname);
\r
697 byte *VLoadImageBuf(char *fname)
\r
700 DetermineFileType(fname);
\r
703 case 0: return LoadPCXBuf(fname); break;
\r
704 case 1: return LoadGIFBuf(fname); break;
\r
705 case 2: return LoadBMPBuf(fname); break;
\r
706 default: err("%s: Unrecognized image type.",fname);
\r