2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
19 static char rcsid[] = "$Id: iff.c,v 1.6 2002-10-04 07:14:31 btb Exp $";
22 #define COMPRESS 1 //do the RLE or not? (for debugging mostly)
23 #define WRITE_TINY 0 //should we write a TINY chunk?
25 #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide
34 //#include "nocfile.h"
38 //Internal constants and structures for this library
40 //Type values for bitmaps
51 #define mskHasTransparentColor 2
53 //Palette entry structure
54 typedef struct pal_entry {byte r,g,b;} pal_entry;
56 //structure of the header in the file
57 typedef struct iff_bitmap_header {
58 short w,h; //width and height of this bitmap
59 short x,y; //generally unused
60 short type; //see types above
61 short transparentcolor; //which color is transparent (if any)
62 short pagewidth,pageheight; //width & height of source screen
63 byte nplanes; //number of planes (8 for 256 color image)
64 byte masking,compression; //see constants above
65 byte xaspect,yaspect; //aspect ratio (usually 5/6)
66 pal_entry palette[256]; //the palette for this bitmap
67 ubyte *raw_data; //ptr to array of data
68 short row_size; //offset to next row
71 ubyte iff_transparent_color;
72 ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid
74 typedef struct fake_file {
80 #define MIN(a,b) ((a<b)?a:b)
82 #define MAKE_SIG(a,b,c,d) (((long)(a)<<24)+((long)(b)<<16)+((c)<<8)+(d))
84 #define form_sig MAKE_SIG('F','O','R','M')
85 #define ilbm_sig MAKE_SIG('I','L','B','M')
86 #define body_sig MAKE_SIG('B','O','D','Y')
87 #define pbm_sig MAKE_SIG('P','B','M',' ')
88 #define bmhd_sig MAKE_SIG('B','M','H','D')
89 #define anhd_sig MAKE_SIG('A','N','H','D')
90 #define cmap_sig MAKE_SIG('C','M','A','P')
91 #define tiny_sig MAKE_SIG('T','I','N','Y')
92 #define anim_sig MAKE_SIG('A','N','I','M')
93 #define dlta_sig MAKE_SIG('D','L','T','A')
96 //void printsig(long s)
98 // char *t=(char *) &s;
100 ///* printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
101 // printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
105 long get_sig(FFILE *f)
109 // if ((s[3]=cfgetc(f))==EOF) return(EOF);
110 // if ((s[2]=cfgetc(f))==EOF) return(EOF);
111 // if ((s[1]=cfgetc(f))==EOF) return(EOF);
112 // if ((s[0]=cfgetc(f))==EOF) return(EOF);
114 #ifndef WORDS_BIGENDIAN
115 if (f->position>=f->length) return EOF;
116 s[3] = f->data[f->position++];
117 if (f->position>=f->length) return EOF;
118 s[2] = f->data[f->position++];
119 if (f->position>=f->length) return EOF;
120 s[1] = f->data[f->position++];
121 if (f->position>=f->length) return EOF;
122 s[0] = f->data[f->position++];
124 if (f->position>=f->length) return EOF;
125 s[0] = f->data[f->position++];
126 if (f->position>=f->length) return EOF;
127 s[1] = f->data[f->position++];
128 if (f->position>=f->length) return EOF;
129 s[2] = f->data[f->position++];
130 if (f->position>=f->length) return EOF;
131 s[3] = f->data[f->position++];
134 return(*((long *) s));
137 int put_sig(long sig,FILE *f)
139 char *s = (char *) &sig;
144 return fputc(s[0],f);
148 char get_byte(FFILE *f)
151 return f->data[f->position++];
154 int put_byte(unsigned char c,FILE *f)
159 int get_word(FFILE *f)
166 if (f->position>=f->length) return EOF;
167 c1 = f->data[f->position++];
168 if (f->position>=f->length) return EOF;
169 c0 = f->data[f->position++];
171 if (c0==0xff) return(EOF);
173 return(((int)c1<<8) + c0);
177 int put_word(int n,FILE *f)
181 c0 = (n & 0xff00) >> 8;
185 return put_byte(c1,f);
188 int put_long(long n,FILE *f)
192 n0 = (int) ((n & 0xffff0000l) >> 16);
193 n1 = (int) (n & 0xffff);
196 return put_word(n1,f);
200 long get_long(FFILE *f)
202 unsigned char c0,c1,c2,c3;
209 if (f->position>=f->length) return EOF;
210 c3 = f->data[f->position++];
211 if (f->position>=f->length) return EOF;
212 c2 = f->data[f->position++];
213 if (f->position>=f->length) return EOF;
214 c1 = f->data[f->position++];
215 if (f->position>=f->length) return EOF;
216 c0 = f->data[f->position++];
218 //printf("get_long %x %x %x %x\n",c3,c2,c1,c0);
220 // if (c0==0xff) return(EOF);
222 return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0);
226 int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader)
228 len++; /* so no "parm not used" warning */
230 // debug("parsing bmhd len=%ld\n",len);
232 bmheader->w = get_word(ifile);
233 bmheader->h = get_word(ifile);
234 bmheader->x = get_word(ifile);
235 bmheader->y = get_word(ifile);
237 bmheader->nplanes = get_byte(ifile);
238 bmheader->masking = get_byte(ifile);
239 bmheader->compression = get_byte(ifile);
240 get_byte(ifile); /* skip pad */
242 bmheader->transparentcolor = get_word(ifile);
243 bmheader->xaspect = get_byte(ifile);
244 bmheader->yaspect = get_byte(ifile);
246 bmheader->pagewidth = get_word(ifile);
247 bmheader->pageheight = get_word(ifile);
249 iff_transparent_color = bmheader->transparentcolor;
251 iff_has_transparency = 0;
253 if (bmheader->masking == mskHasTransparentColor)
254 iff_has_transparency = 1;
256 else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
257 return IFF_UNKNOWN_MASK;
259 // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
260 // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
266 // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
267 int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader)
269 unsigned char *p=bmheader->raw_data;
272 int nn,wid_cnt,end_cnt,plane;
274 unsigned char *data_end;
283 end_pos = ifile->position + len;
287 if (bmheader->type == TYPE_PBM) {
290 } else if (bmheader->type == TYPE_ILBM) {
291 width = (bmheader->w+7)/8;
292 depth=bmheader->nplanes;
295 end_cnt = (width&1)?-1:0;
297 data_end = p + width*bmheader->h*depth;
299 if (bmheader->compression == cmpNone) { /* no compression */
302 for (y=bmheader->h;y;y--) {
304 // for (x=bmheader->w;x;x--) *p++=cfgetc(ifile);
305 // cfread(p, bmheader->w, 1, ifile);
308 for (x=0;x<width*depth;x++)
309 *p++=ifile->data[ifile->position++];
311 if (bmheader->masking == mskHasMask)
312 ifile->position += width; //skip mask!
314 // if (bmheader->w & 1) ignore = cfgetc(ifile);
315 if (bmheader->w & 1) ifile->position++;
318 //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
321 else if (bmheader->compression == cmpByteRun1)
322 for (wid_cnt=width,plane=0;ifile->position<end_pos && p<data_end;) {
325 // if (old_cnt-cnt > 2048) {
330 if (wid_cnt == end_cnt) {
333 if ((bmheader->masking == mskHasMask && plane==depth+1) ||
334 (bmheader->masking != mskHasMask && plane==depth))
338 Assert(wid_cnt > end_cnt);
341 n=ifile->data[ifile->position++];
343 if (n >= 0) { // copy next n+1 bytes from source, they are not compressed
346 if (wid_cnt==-1) {--nn; Assert(width&1);}
347 if (plane==depth) //masking row
348 ifile->position += nn;
350 while (nn--) *p++=ifile->data[ifile->position++];
351 if (wid_cnt==-1) ifile->position++;
353 else if (n>=-127) { // next -n + 1 bytes are following byte
354 c=ifile->data[ifile->position++];
357 if (wid_cnt==-1) {--nn; Assert(width&1);}
358 if (plane!=depth) //not masking row
359 {memset(p,c,nn); p+=nn;}
363 if ((p-bmheader->raw_data) % width == 0)
366 Assert((p-bmheader->raw_data) - (width*row_count) < width);
371 if (p!=data_end) //if we don't have the whole bitmap...
372 return IFF_CORRUPT; //...the give an error
374 //Pretend we read the whole chuck, because if we didn't, it's because
375 //we didn't read the last mask like or the last rle record for padding
376 //or whatever and it's not important, because we check to make sure
377 //we got the while bitmap, and that's what really counts.
379 ifile->position = end_pos;
381 if (ignore) ignore++; // haha, suppress the evil warning message
386 //modify passed bitmap
387 int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader)
389 unsigned char *p=bmheader->raw_data;
391 long chunk_end = ifile->position + len;
393 get_long(ifile); //longword, seems to be equal to 4. Don't know what it is
395 for (y=0;y<bmheader->h;y++) {
397 int cnt = bmheader->w;
400 n_items = get_byte(ifile);
404 code = get_byte(ifile);
406 if (code==0) { //repeat
409 rep = get_byte(ifile);
410 val = get_byte(ifile);
418 else if (code > 0x80) { //skip
430 *p++ = get_byte(ifile);
446 if (ifile->position == chunk_end-1) //pad
449 if (ifile->position != chunk_end)
455 // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
456 void skip_chunk(FFILE *ifile,long len)
462 //printf( "Skipping %d chunk\n", ilen );
464 ifile->position += ilen;
466 if (ifile->position >= ifile->length ) {
467 ifile->position = ifile->length;
471 // for (i=0; i<ilen; i++ )
472 // c = cfgetc(ifile);
473 //Assert(cfseek(ifile,ilen,SEEK_CUR)==0);
476 //read an ILBM or PBM file
477 // Pass pointer to opened file, and to empty bitmap_header structure, and form length
478 int iff_parse_ilbm_pbm(FFILE *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
482 long start_pos,end_pos;
484 start_pos = ifile->position;
485 end_pos = start_pos-4+form_len;
487 // printf(" %ld ",form_len);
488 // printsig(form_type);
491 if (form_type == pbm_sig)
492 bmheader->type = TYPE_PBM;
494 bmheader->type = TYPE_ILBM;
496 while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) {
503 // printf(" %ld\n",len);
509 int save_w=bmheader->w,save_h=bmheader->h;
511 //printf("Parsing header\n");
513 ret = parse_bmhd(ifile,len,bmheader);
515 if (ret != IFF_NO_ERROR)
518 if (bmheader->raw_data) {
520 if (save_w != bmheader->w || save_h != bmheader->h)
521 return IFF_BM_MISMATCH;
526 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
527 if (!bmheader->raw_data)
537 if (!prev_bm) return IFF_CORRUPT;
539 bmheader->w = prev_bm->bm_w;
540 bmheader->h = prev_bm->bm_h;
541 bmheader->type = prev_bm->bm_type;
543 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
545 memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h );
546 skip_chunk(ifile,len);
552 int ncolors=(int) (len/3),cnum;
555 //printf("Parsing RGB map\n");
556 for (cnum=0;cnum<ncolors;cnum++) {
560 r = ifile->data[ifile->position++];
561 g = ifile->data[ifile->position++];
562 b = ifile->data[ifile->position++];
563 r >>= 2; bmheader->palette[cnum].r = r;
564 g >>= 2; bmheader->palette[cnum].g = g;
565 b >>= 2; bmheader->palette[cnum].b = b;
567 //if (len & 1) ignore = cfgetc(ifile);
568 if (len & 1 ) ifile->position++;
576 //printf("Parsing body\n");
577 if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
584 if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
589 skip_chunk(ifile,len);
594 //if (ignore) ignore++;
596 if (ifile->position != start_pos-4+form_len)
599 return IFF_NO_ERROR; /* ok! */
602 //convert an ILBM file to a PBM file
603 int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
606 byte *new_data,*destptr,*rowptr;
607 int bytes_per_row,byteofs;
608 ubyte checkmask,newbyte,setbit;
610 MALLOC( new_data, byte, bmheader->w * bmheader->h );
611 if (new_data == NULL) return IFF_NO_MEM;
615 bytes_per_row = 2*((bmheader->w+15)/16);
617 for (y=0;y<bmheader->h;y++) {
619 rowptr = &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes];
621 for (x=0,checkmask=0x80;x<bmheader->w;x++) {
625 for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
627 if (rowptr[bytes_per_row * p + byteofs] & checkmask)
633 *destptr++ = newbyte;
635 if ((checkmask >>= 1) == 0) checkmask=0x80;
640 d_free(bmheader->raw_data);
641 bmheader->raw_data = new_data;
643 bmheader->type = TYPE_PBM;
648 #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
650 int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader)
657 palptr = bmheader->palette;
659 // if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL)
660 // {ret=IFF_NO_MEM; goto done;}
661 MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2);
662 if (new_data == NULL)
665 for (y=0; y<bm->bm_h; y++) {
667 for (x=0; x<bmheader->w; x++)
668 new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]);
672 d_free(bm->bm_data); //get rid of old-style data
673 bm->bm_data = (ubyte *) new_data; //..and point to new data
675 bm->bm_rowsize *= 2; //two bytes per row
681 //read in a entire file into a fake file structure
682 int open_fake_file(char *ifilename,FFILE *ffile)
687 //printf( "Reading %s\n", ifilename );
691 if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE;
693 ffile->length = cfilelength(ifile);
695 MALLOC(ffile->data,ubyte,ffile->length);
697 if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length)
698 ret = IFF_READ_ERROR;
704 if (ifile) cfclose(ifile);
709 void close_fake_file(FFILE *f)
717 //copy an iff header structure to a grs_bitmap structure
718 void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader)
720 bm->bm_x = bm->bm_y = 0;
721 bm->bm_w = bmheader->w;
722 bm->bm_h = bmheader->h;
723 bm->bm_type = bmheader->type;
724 bm->bm_rowsize = bmheader->w;
725 bm->bm_data = bmheader->raw_data;
727 bm->bm_flags = bm->bm_handle = 0;
731 //if bm->bm_data is set, use it (making sure w & h are correct), else
732 //allocate the memory
733 int iff_parse_bitmap(FFILE *ifile,grs_bitmap *bm,int bitmap_type,byte *palette,grs_bitmap *prev_bm)
735 int ret; //return code
736 iff_bitmap_header bmheader;
740 bmheader.raw_data = bm->bm_data;
742 if (bmheader.raw_data) {
743 bmheader.w = bm->bm_w;
744 bmheader.h = bm->bm_h;
749 if (sig != form_sig) {
753 form_len = get_long(ifile);
755 form_type = get_sig(ifile);
757 if (form_type == anim_sig)
759 else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
760 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
762 ret = IFF_UNKNOWN_FORM;
764 if (ret != IFF_NO_ERROR) { //got an error parsing
765 if (bmheader.raw_data) d_free(bmheader.raw_data);
769 //If IFF file is ILBM, convert to PPB
770 if (bmheader.type == TYPE_ILBM) {
772 ret = convert_ilbm_to_pbm(&bmheader);
774 if (ret != IFF_NO_ERROR)
778 //Copy data from iff_bitmap_header structure into grs_bitmap structure
780 copy_iff_to_grs(bm,&bmheader);
783 if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette));
785 // if (palette) memcpy(palette,&bmheader.palette, 768); // pal_entry is 4 bytes on mac
791 for (i = 0; i < 256; i++) {
792 *c++ = bmheader.palette[i].r;
793 *c++ = bmheader.palette[i].g;
794 *c++ = bmheader.palette[i].b;
799 //Now do post-process if required
801 if (bitmap_type == BM_RGB15) {
802 ret = convert_rgb15(bm,&bmheader);
803 if (ret != IFF_NO_ERROR)
811 //returns error codes - see IFF.H. see GR.H for bitmap_type
812 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
814 int ret; //return code
817 ret = open_fake_file(ifilename,&ifile); //read in entire file
818 if (ret == IFF_NO_ERROR) {
820 ret = iff_parse_bitmap(&ifile,bm,bitmap_type,palette,NULL);
823 if (ifile.data) d_free(ifile.data);
825 close_fake_file(&ifile);
832 //like iff_read_bitmap(), but reads into a bitmap that already exists,
833 //without allocating memory for the bitmap.
834 int iff_read_into_bitmap(char *ifilename,grs_bitmap *bm,byte *palette)
836 int ret; //return code
839 ret = open_fake_file(ifilename,&ifile); //read in entire file
840 if (ret == IFF_NO_ERROR) {
841 ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
844 if (ifile.data) d_free(ifile.data);
846 close_fake_file(&ifile);
855 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
857 put_sig(bmhd_sig,ofile);
858 put_long((long) BMHD_SIZE,ofile);
860 put_word(bitmap_header->w,ofile);
861 put_word(bitmap_header->h,ofile);
862 put_word(bitmap_header->x,ofile);
863 put_word(bitmap_header->y,ofile);
865 put_byte(bitmap_header->nplanes,ofile);
866 put_byte(bitmap_header->masking,ofile);
867 put_byte(bitmap_header->compression,ofile);
868 put_byte(0,ofile); /* pad */
870 put_word(bitmap_header->transparentcolor,ofile);
871 put_byte(bitmap_header->xaspect,ofile);
872 put_byte(bitmap_header->yaspect,ofile);
874 put_word(bitmap_header->pagewidth,ofile);
875 put_word(bitmap_header->pageheight,ofile);
881 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
885 int n_colors = 1<<bitmap_header->nplanes;
887 put_sig(cmap_sig,ofile);
888 // put_long(sizeof(pal_entry) * n_colors,ofile);
889 put_long(3 * n_colors,ofile);
891 //printf("new write pal %d %d\n",3,n_colors);
893 for (i=0; i<256; i++) {
895 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
896 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
897 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
903 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
904 // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
909 int rle_span(ubyte *dest,ubyte *src,int len)
911 int n,lit_cnt,rep_cnt;
912 ubyte last,*cnt_ptr,*dptr;
918 last=src[0]; lit_cnt=1;
920 for (n=1;n<len;n++) {
922 if (src[n] == last) {
927 while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
929 if (rep_cnt > 2 || lit_cnt < 2) {
931 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count
932 *dptr++ = -(rep_cnt-1);
935 lit_cnt = (n<len)?1:0;
937 continue; //go to next char
945 cnt_ptr = dptr++; //save place for count
946 *dptr++=last; //store first char
949 *dptr++ = last = src[n];
951 if (lit_cnt == 127) {
965 *dptr++=last; //store first char
967 else if (lit_cnt > 1)
968 *cnt_ptr = lit_cnt-1;
973 #define EVEN(a) ((a+1)&0xfffffffel)
975 //returns length of chunk
976 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
978 int w=bitmap_header->w,h=bitmap_header->h;
980 long len = EVEN(w) * h,newlen,total_len=0;
981 ubyte *p=bitmap_header->raw_data,*new_span;
984 put_sig(body_sig,ofile);
985 save_pos = ftell(ofile);
988 //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
989 MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
990 if (new_span == NULL) return IFF_NO_MEM;
992 for (y=bitmap_header->h;y--;) {
994 if (compression_on) {
995 total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
996 fwrite(new_span,newlen,1,ofile);
999 fwrite(p,bitmap_header->w+odd,1,ofile);
1001 p+=bitmap_header->row_size; //bitmap_header->w;
1004 if (compression_on) { //write actual data length
1005 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1006 put_long(total_len,ofile);
1007 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1008 if (total_len&1) fputc(0,ofile); //pad to even
1013 return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1018 //write a small representation of a bitmap. returns size
1019 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1023 int len,total_len=0,newlen;
1025 ubyte *p = bitmap_header->raw_data;
1026 ubyte tspan[80],new_span[80*2];
1029 skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1031 new_w = bitmap_header->w / skip;
1032 new_h = bitmap_header->h / skip;
1036 len = new_w * new_h + 4;
1038 put_sig(tiny_sig,ofile);
1039 save_pos = cftell(ofile);
1040 put_long(EVEN(len),ofile);
1042 put_word(new_w,ofile);
1043 put_word(new_h,ofile);
1045 for (y=0;y<new_h;y++) {
1046 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1049 if (compression_on) {
1050 total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1051 fwrite(new_span,newlen,1,ofile);
1054 fwrite(p,new_w+odd,1,ofile);
1056 p += skip * bitmap_header->row_size; //bitmap_header->w;
1060 if (compression_on) {
1061 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1062 put_long(4+total_len,ofile);
1063 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1064 if (total_len&1) cfputc(0,ofile); //pad to even
1067 return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1071 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */
1074 long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1075 long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1078 //printf("write_pbm\n");
1080 put_sig(form_sig,ofile);
1081 save_pos = ftell(ofile);
1082 put_long(pbm_size+8,ofile);
1083 put_sig(pbm_sig,ofile);
1085 ret = write_bmhd(ofile,bitmap_header);
1086 if (ret != IFF_NO_ERROR) return ret;
1088 ret = write_pal(ofile,bitmap_header);
1089 if (ret != IFF_NO_ERROR) return ret;
1092 tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1097 body_size = write_body(ofile,bitmap_header,compression_on);
1099 pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1101 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1102 put_long(pbm_size+8,ofile);
1103 Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1109 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1110 //returns error codes - see IFF.H.
1111 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1114 iff_bitmap_header bmheader;
1118 if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1121 compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1126 //fill in values in bmheader
1128 bmheader.x = bmheader.y = 0;
1129 bmheader.w = bm->bm_w;
1130 bmheader.h = bm->bm_h;
1131 bmheader.type = TYPE_PBM;
1132 bmheader.transparentcolor = iff_transparent_color;
1133 bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write
1134 bmheader.pageheight = bm->bm_h;
1135 bmheader.nplanes = 8;
1136 bmheader.masking = mskNone;
1137 if (iff_has_transparency) {
1138 bmheader.masking |= mskHasTransparentColor;
1140 bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1142 bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write
1143 bmheader.raw_data = bm->bm_data;
1144 bmheader.row_size = bm->bm_rowsize;
1146 if (palette) memcpy(&bmheader.palette,palette,256*3);
1148 //open file and write
1150 if ((ofile = fopen(ofilename,"wb")) == NULL) {
1153 ret = write_pbm(ofile,&bmheader,compression_on);
1161 //read in many brushes. fills in array of pointers, and n_bitmaps.
1162 //returns iff error codes
1163 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1165 int ret; //return code
1167 iff_bitmap_header bmheader;
1173 ret = open_fake_file(ifilename,&ifile); //read in entire file
1174 if (ret != IFF_NO_ERROR) goto done;
1176 bmheader.raw_data = NULL;
1178 sig=get_sig(&ifile);
1179 form_len = get_long(&ifile);
1181 if (sig != form_sig) {
1186 form_type = get_sig(&ifile);
1188 if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1189 ret = IFF_FORM_BITMAP;
1190 else if (form_type == anim_sig) {
1191 int anim_end = ifile.position + form_len - 4;
1193 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1195 grs_bitmap *prev_bm;
1197 prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1199 MALLOC(bm_list[*n_bitmaps] , grs_bitmap, 1 );
1200 bm_list[*n_bitmaps]->bm_data = NULL;
1202 ret = iff_parse_bitmap(&ifile,bm_list[*n_bitmaps],form_type,*n_bitmaps>0?NULL:palette,prev_bm);
1204 if (ret != IFF_NO_ERROR)
1210 if (ifile.position < anim_end) //ran out of room
1211 ret = IFF_TOO_MANY_BMS;
1215 ret = IFF_UNKNOWN_FORM;
1219 close_fake_file(&ifile);
1225 //text for error messges
1226 char error_messages[] = {
1228 "Not enough mem for loading or processing bitmap.\0"
1229 "IFF file has unknown FORM type.\0"
1230 "Not an IFF file.\0"
1231 "Cannot open file.\0"
1232 "Tried to save invalid type, like BM_RGB15.\0"
1233 "Bad data in file.\0"
1234 "ANIM file cannot be loaded with normal bitmap loader.\0"
1235 "Normal bitmap file cannot be loaded with anim loader.\0"
1236 "Array not big enough on anim brush read.\0"
1237 "Unknown mask type in bitmap header.\0"
1238 "Error reading file.\0"
1242 //function to return pointer to error message
1243 char *iff_errormsg(int error_number)
1245 char *p = error_messages;
1247 while (error_number--) {
1249 if (!p) return NULL;