1 /*
2 * xvbmp.c - I/O routines for .BMP files (MS Windows 3.x and later; OS/2)
3 *
4 * LoadBMP(fname, numcols)
5 * WriteBMP(fp, pic, ptype, w, h, r, g, b, numcols, style);
6 */
7
8 #include "copyright.h"
9
10 #include "xv.h"
11
12 /* Comments on error-handling:
13 A truncated file is not considered a Major Error. The file is loaded,
14 and the rest of the pic is filled with 0's.
15
16 A file with garbage characters in it is an unloadable file. All allocated
17 stuff is tossed, and LoadBMP returns non-zero.
18
19 Not being able to malloc is a Fatal Error. The program is aborted. */
20
21
22 #define BI_RGB 0 /* a.k.a. uncompressed */
23 #define BI_RLE8 1
24 #define BI_RLE4 2
25 #define BI_BITFIELDS 3 /* BMP version 4 */
26 #define BI_JPEG 4 /* BMP version 5 (not yet supported) */
27 #define BI_PNG 5 /* BMP version 5 (not yet supported) */
28
29 #define WIN_OS2_OLD 12
30 #define WIN_NEW 40
31 #define OS2_NEW 64
32
33 #if (defined(UINT_MAX) && UINT_MAX != 0xffffffffU)
34 # error XV's BMP code requires 32-bit unsigned integer type, but u_int isn't
35 #endif
36
37 static long filesize;
38
39 static int loadBMP1 PARM((FILE *, byte *, u_int, u_int));
40 static int loadBMP4 PARM((FILE *, byte *, u_int, u_int, u_int));
41 static int loadBMP8 PARM((FILE *, byte *, u_int, u_int, u_int));
42 static int loadBMP16 PARM((FILE *, byte *, u_int, u_int, u_int *));
43 static int loadBMP24 PARM((FILE *, byte *, u_int, u_int, u_int));
44 static int loadBMP32 PARM((FILE *, byte *, u_int, u_int, u_int *));
45 static u_int getshort PARM((FILE *));
46 static u_int getint PARM((FILE *));
47 static void putshort PARM((FILE *, int));
48 static void putint PARM((FILE *, int));
49 static void writeBMP1 PARM((FILE *, byte *, int, int));
50 static void writeBMP4 PARM((FILE *, byte *, int, int));
51 static void writeBMP8 PARM((FILE *, byte *, int, int));
52 static void writeBMP24 PARM((FILE *, byte *, int, int));
53 static int bmpError PARM((const char *, const char *));
54
55
56 #define FERROR(fp) (ferror(fp) || feof(fp))
57
58 /*******************************************/
LoadBMP(fname,pinfo)59 int LoadBMP(fname, pinfo)
60 char *fname;
61 PICINFO *pinfo;
62 /*******************************************/
63 {
64 FILE *fp;
65 int i, c, c1, rv, bPad;
66 u_int bfSize, bfOffBits, biSize, biWidth, biHeight, biPlanes;
67 u_int biBitCount, biCompression, biSizeImage, biXPelsPerMeter;
68 u_int biYPelsPerMeter, biClrUsed, biClrImportant;
69 u_int colormask[3];
70 char buf[512], rgb_bits[16];
71 const char *cmpstr, *bname;
72 byte *pic24, *pic8;
73
74 /* returns '1' on success */
75
76 pic8 = pic24 = (byte *) NULL;
77 bname = BaseName(fname);
78
79 fp = xv_fopen(fname,"r");
80 if (!fp) return (bmpError(bname, "couldn't open file"));
81
82 fseek(fp, 0L, 2); /* figure out the file size */
83 filesize = ftell(fp);
84 fseek(fp, 0L, 0);
85
86
87 /* read the file type (first two bytes) */
88 c = getc(fp); c1 = getc(fp);
89 if (c!='B' || c1!='M') { bmpError(bname,"file type != 'BM'"); goto ERROR; }
90
91 bfSize = getint(fp);
92 getshort(fp); /* reserved and ignored */
93 getshort(fp);
94 bfOffBits = getint(fp);
95
96 biSize = getint(fp);
97
98 if (biSize == WIN_NEW || biSize == OS2_NEW) {
99 biWidth = getint(fp);
100 biHeight = getint(fp);
101 biPlanes = getshort(fp);
102 biBitCount = getshort(fp);
103 biCompression = getint(fp);
104 biSizeImage = getint(fp);
105 biXPelsPerMeter = getint(fp);
106 biYPelsPerMeter = getint(fp);
107 biClrUsed = getint(fp);
108 biClrImportant = getint(fp);
109 }
110 else { /* old bitmap format */
111 biWidth = getshort(fp); /* Types have changed ! */
112 biHeight = getshort(fp);
113 biPlanes = getshort(fp);
114 biBitCount = getshort(fp);
115
116 /* not in old versions, so have to compute them */
117 biSizeImage = (((biPlanes * biBitCount*biWidth)+31)/32)*4*biHeight;
118
119 biCompression = BI_RGB;
120 biXPelsPerMeter = biYPelsPerMeter = 0;
121 biClrUsed = biClrImportant = 0;
122 }
123
124 if (DEBUG>1) {
125 fprintf(stderr,"\nLoadBMP:\tbfSize=%d, bfOffBits=%d\n",bfSize,bfOffBits);
126 fprintf(stderr,"\t\tbiSize=%d, biWidth=%d, biHeight=%d, biPlanes=%d\n",
127 biSize, biWidth, biHeight, biPlanes);
128 fprintf(stderr,"\t\tbiBitCount=%d, biCompression=%d, biSizeImage=%d\n",
129 biBitCount, biCompression, biSizeImage);
130 fprintf(stderr,"\t\tbiX,YPelsPerMeter=%d,%d biClrUsed=%d, biClrImp=%d\n",
131 biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant);
132 }
133
134 if (FERROR(fp)) { bmpError(bname,"EOF reached in file header"); goto ERROR; }
135
136
137 /* error-checking */
138 if ((biBitCount!=1 && biBitCount!=4 && biBitCount!=8 &&
139 biBitCount!=16 && biBitCount!=24 && biBitCount!=32) ||
140 biPlanes!=1 || biCompression>BI_PNG ||
141 biWidth<=0 || biHeight<=0 ||
142 (biClrUsed && biClrUsed > (1 << biBitCount))) {
143
144 sprintf(buf,
145 "Unsupported BMP type (%dx%d, Bits=%d, Colors=%d, Planes=%d, "
146 "Compr=%d)",
147 biWidth, biHeight, biBitCount, biClrUsed, biPlanes, biCompression);
148
149 bmpError(bname, buf);
150 goto ERROR;
151 }
152
153 if (biCompression>BI_BITFIELDS) {
154 sprintf(buf, "Unsupported BMP compression method (%s)",
155 biCompression == BI_JPEG? "JPEG" :
156 biCompression == BI_PNG? "PNG" :
157 "unknown/newer than v5");
158
159 bmpError(bname, buf);
160 goto ERROR;
161 }
162
163 if (((biBitCount==1 || biBitCount==24) && biCompression != BI_RGB) ||
164 (biBitCount==4 && biCompression!=BI_RGB && biCompression!=BI_RLE4) ||
165 (biBitCount==8 && biCompression!=BI_RGB && biCompression!=BI_RLE8) ||
166 ((biBitCount==16 || biBitCount==32) &&
167 biCompression!=BI_RGB && biCompression!=BI_BITFIELDS)) {
168
169 sprintf(buf,"Unsupported BMP type (bitCount=%d, Compression=%d)",
170 biBitCount, biCompression);
171
172 bmpError(bname, buf);
173 goto ERROR;
174 }
175
176
177 bPad = 0;
178 if (biSize != WIN_OS2_OLD) {
179 /* skip ahead to colormap, using biSize */
180 c = biSize - 40; /* 40 bytes read from biSize to biClrImportant */
181 for (i=0; i<c; i++)
182 getc(fp);
183 bPad = bfOffBits - (biSize + 14);
184 }
185
186 /* 16-bit or 32-bit color mask */
187 if (biCompression==BI_BITFIELDS) {
188 colormask[0] = getint(fp);
189 colormask[1] = getint(fp);
190 colormask[2] = getint(fp);
191 bPad -= 12;
192 }
193
194 /* load up colormap, if any */
195 if (biBitCount == 1 || biBitCount == 4 || biBitCount == 8) {
196 int i, cmaplen;
197
198 cmaplen = (biClrUsed) ? biClrUsed : 1 << biBitCount;
199 for (i=0; i<cmaplen; i++) {
200 pinfo->b[i] = getc(fp);
201 pinfo->g[i] = getc(fp);
202 pinfo->r[i] = getc(fp);
203 if (biSize != WIN_OS2_OLD) {
204 getc(fp);
205 bPad -= 4;
206 }
207 }
208
209 if (FERROR(fp))
210 { bmpError(bname,"EOF reached in BMP colormap"); goto ERROR; }
211
212 if (DEBUG>1) {
213 fprintf(stderr,"LoadBMP: BMP colormap: (RGB order)\n");
214 for (i=0; i<cmaplen; i++) {
215 fprintf(stderr,"%02x%02x%02x ", pinfo->r[i],pinfo->g[i],pinfo->b[i]);
216 }
217 fprintf(stderr,"\n\n");
218 }
219 }
220
221 if (biSize != WIN_OS2_OLD) {
222 /* Waste any unused bytes between the colour map (if present)
223 and the start of the actual bitmap data. */
224
225 while (bPad > 0) {
226 (void) getc(fp);
227 bPad--;
228 }
229 }
230
231 /* create pic8 or pic24 */
232
233 if (biBitCount==16 || biBitCount==24 || biBitCount==32) {
234 u_int npixels = biWidth * biHeight;
235 u_int count = 3 * npixels;
236
237 if (biWidth == 0 || biHeight == 0 || npixels/biWidth != biHeight ||
238 count/3 != npixels)
239 return (bmpError(bname, "image dimensions too large"));
240 pic24 = (byte *) calloc((size_t) count, (size_t) 1);
241 if (!pic24) return (bmpError(bname, "couldn't malloc 'pic24'"));
242 }
243 else {
244 u_int npixels = biWidth * biHeight;
245
246 if (biWidth == 0 || biHeight == 0 || npixels/biWidth != biHeight)
247 return (bmpError(bname, "image dimensions too large"));
248 pic8 = (byte *) calloc((size_t) npixels, (size_t) 1);
249 if (!pic8) return(bmpError(bname, "couldn't malloc 'pic8'"));
250 }
251
252 WaitCursor();
253
254 /* load up the image */
255 switch (biBitCount) {
256 case 1:
257 rv = loadBMP1(fp, pic8, biWidth, biHeight);
258 break;
259 case 4:
260 rv = loadBMP4(fp, pic8, biWidth, biHeight, biCompression);
261 break;
262 case 8:
263 rv = loadBMP8(fp, pic8, biWidth, biHeight, biCompression);
264 break;
265 case 16:
266 rv = loadBMP16(fp, pic24, biWidth, biHeight, /* v-- BI_RGB */
267 biCompression == BI_BITFIELDS? colormask : NULL);
268 break;
269 default:
270 if (biBitCount == 32 && biCompression == BI_BITFIELDS)
271 rv = loadBMP32(fp, pic24, biWidth, biHeight, colormask);
272 else /* 24 or (32 and BI_RGB) */
273 rv = loadBMP24(fp, pic24, biWidth, biHeight, biBitCount);
274 break;
275 }
276
277 if (rv) bmpError(bname, "File appears truncated. Winging it.");
278
279
280 fclose(fp);
281
282
283 if (biBitCount > 8) {
284 pinfo->pic = pic24;
285 pinfo->type = PIC24;
286 }
287 else {
288 pinfo->pic = pic8;
289 pinfo->type = PIC8;
290 }
291
292 cmpstr = "";
293 if (biCompression == BI_RLE4) cmpstr = ", RLE4 compressed";
294 else if (biCompression == BI_RLE8) cmpstr = ", RLE8 compressed";
295 else if (biCompression == BI_BITFIELDS) {
296 int bit, c[3], i;
297 u_int mask;
298
299 for (i = 0; i < 3; ++i) {
300 mask = colormask[i];
301 c[i] = 0;
302 for (bit = 0; bit < 32; ++bit) {
303 if (mask & 1)
304 ++c[i];
305 mask >>= 1;
306 }
307 }
308 sprintf(rgb_bits, ", RGB%d%d%d", c[0], c[1], c[2]);
309 cmpstr = rgb_bits;
310 }
311
312 pinfo->w = biWidth; pinfo->h = biHeight;
313 pinfo->normw = pinfo->w; pinfo->normh = pinfo->h;
314 pinfo->frmType = F_BMP;
315 pinfo->colType = F_FULLCOLOR;
316
317 sprintf(pinfo->fullInfo, "%sBMP, %d bit%s per pixel%s. (%ld bytes)",
318 ((biSize==WIN_OS2_OLD) ? "Old OS/2 " :
319 (biSize==WIN_NEW) ? "Windows " : ""),
320 biBitCount, (biBitCount == 1) ? "" : "s",
321 cmpstr, filesize);
322
323 sprintf(pinfo->shrtInfo, "%dx%d BMP.", biWidth, biHeight);
324 pinfo->comment = (char *) NULL;
325
326 return 1;
327
328
329 ERROR:
330 fclose(fp);
331 return 0;
332 }
333
334
335 /*******************************************/
loadBMP1(fp,pic8,w,h)336 static int loadBMP1(fp, pic8, w, h)
337 FILE *fp;
338 byte *pic8;
339 u_int w,h;
340 {
341 int i,j,c,bitnum,padw;
342 byte *pp = pic8 + ((h - 1) * w);
343 size_t l = w*h;
344
345 c = 0;
346 padw = ((w + 31)/32) * 32; /* 'w', padded to be a multiple of 32 */
347
348 for (i=h-1; i>=0 && (pp - pic8 <= l); i--) {
349 pp = pic8 + (i * w);
350 if ((i&0x3f)==0) WaitCursor();
351 for (j=bitnum=0; j<padw; j++,bitnum++) {
352 if ((bitnum&7) == 0) { /* read the next byte */
353 c = getc(fp);
354 bitnum = 0;
355 }
356
357 if (j<w) {
358 *pp++ = (c & 0x80) ? 1 : 0;
359 c <<= 1;
360 }
361 }
362 if (FERROR(fp)) break;
363 }
364
365 return (FERROR(fp));
366 }
367
368
369
370 /*******************************************/
loadBMP4(fp,pic8,w,h,comp)371 static int loadBMP4(fp, pic8, w, h, comp)
372 FILE *fp;
373 byte *pic8;
374 u_int w,h,comp;
375 {
376 int i,j,c,c1,x,y,nybnum,padw,rv;
377 byte *pp = pic8 + ((h - 1) * w);
378 size_t l = w*h;
379
380 rv = 0;
381 c = c1 = 0;
382
383 if (comp == BI_RGB) { /* read uncompressed data */
384 padw = ((w + 7)/8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */
385
386 for (i=h-1; i>=0 && (pp - pic8 <= l); i--) {
387 pp = pic8 + (i * w);
388 if ((i&0x3f)==0) WaitCursor();
389
390 for (j=nybnum=0; j<padw; j++,nybnum++) {
391 if ((nybnum & 1) == 0) { /* read next byte */
392 c = getc(fp);
393 nybnum = 0;
394 }
395
396 if (j<w) {
397 *pp++ = (c & 0xf0) >> 4;
398 c <<= 4;
399 }
400 }
401 if (FERROR(fp)) break;
402 }
403 }
404
405 else if (comp == BI_RLE4) { /* read RLE4 compressed data */
406 x = y = 0;
407 pp = pic8 + x + (h-y-1)*w;
408
409 while (y<h) {
410 c = getc(fp); if (c == EOF) { rv = 1; break; }
411
412 if (c) { /* encoded mode */
413 c1 = getc(fp);
414 for (i=0; i<c && (pp - pic8 <= l); i++,x++,pp++)
415 *pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
416 }
417
418 else { /* c==0x00 : escape codes */
419 c = getc(fp); if (c == EOF) { rv = 1; break; }
420
421 if (c == 0x00) { /* end of line */
422 x=0; y++; pp = pic8 + x + (h-y-1)*w;
423 }
424
425 else if (c == 0x01) break; /* end of pic8 */
426
427 else if (c == 0x02) { /* delta */
428 c = getc(fp); x += c;
429 c = getc(fp); y += c;
430 pp = pic8 + x + (h-y-1)*w;
431 }
432
433 else { /* absolute mode */
434 for (i=0; i<c && (pp - pic8 <= l); i++, x++, pp++) {
435 if ((i&1) == 0) c1 = getc(fp);
436 *pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
437 }
438
439 if (((c&3)==1) || ((c&3)==2)) getc(fp); /* read pad byte */
440 }
441 } /* escape processing */
442 if (FERROR(fp)) break;
443 } /* while */
444 }
445
446 else {
447 fprintf(stderr,"unknown BMP compression type 0x%0x\n", comp);
448 }
449
450 if (FERROR(fp)) rv = 1;
451 return rv;
452 }
453
454
455
456 /*******************************************/
loadBMP8(fp,pic8,w,h,comp)457 static int loadBMP8(fp, pic8, w, h, comp)
458 FILE *fp;
459 byte *pic8;
460 u_int w,h,comp;
461 {
462 int i,j,c,c1,padw,x,y,rv;
463 byte *pp = pic8 + ((h - 1) * w);
464 size_t l = w*h;
465 byte *pend;
466
467 rv = 0;
468
469 pend = pic8 + w * h;
470
471 if (comp == BI_RGB) { /* read uncompressed data */
472 padw = ((w + 3)/4) * 4; /* 'w' padded to a multiple of 4pix (32 bits) */
473
474 for (i=h-1; i>=0 && (pp - pic8 <= l); i--) {
475 pp = pic8 + (i * w);
476 if ((i&0x3f)==0) WaitCursor();
477
478 for (j=0; j<padw; j++) {
479 c = getc(fp); if (c==EOF) rv = 1;
480 if (j<w) *pp++ = c;
481 }
482 if (FERROR(fp)) break;
483 }
484 }
485
486 else if (comp == BI_RLE8) { /* read RLE8 compressed data */
487 x = y = 0;
488 pp = pic8 + x + (h-y-1)*w;
489
490 while (y<h && pp<=pend) {
491 c = getc(fp); if (c == EOF) { rv = 1; break; }
492
493 if (c) { /* encoded mode */
494 c1 = getc(fp);
495 for (i=0; i<c && pp<=pend; i++,x++,pp++) *pp = c1;
496 }
497
498 else { /* c==0x00 : escape codes */
499 c = getc(fp); if (c == EOF) { rv = 1; break; }
500
501 if (c == 0x00) { /* end of line */
502 x=0; y++; pp = pic8 + x + (h-y-1)*w;
503 }
504
505 else if (c == 0x01) break; /* end of pic8 */
506
507 else if (c == 0x02) { /* delta */
508 c = getc(fp); x += c;
509 c = getc(fp); y += c;
510 pp = pic8 + x + (h-y-1)*w;
511 }
512
513 else { /* absolute mode */
514 for (i=0; i<c && pp<=pend; i++, x++, pp++) {
515 c1 = getc(fp);
516 *pp = c1;
517 }
518
519 if (c & 1) getc(fp); /* odd length run: read an extra pad byte */
520 }
521 } /* escape processing */
522 if (FERROR(fp)) break;
523 } /* while */
524 }
525
526 else {
527 fprintf(stderr,"unknown BMP compression type 0x%0x\n", comp);
528 }
529
530 if (FERROR(fp)) rv = 1;
531 return rv;
532 }
533
534
535
536 /*******************************************/
loadBMP16(fp,pic24,w,h,mask)537 static int loadBMP16(fp, pic24, w, h, mask)
538 FILE *fp;
539 byte *pic24;
540 u_int w, h, *mask;
541 {
542 int x, y;
543 byte *pp = pic24 + ((h - 1) * w * 3);
544 size_t l = w*h*3;
545 u_int buf, colormask[6];
546 int i, bit, bitshift[6], colorbits[6], bitshift2[6];
547
548 if (mask == NULL) { /* RGB555 */
549 colormask[0] = 0x00007c00;
550 colormask[1] = 0x000003e0;
551 colormask[2] = 0x0000001f;
552 colormask[3] = 0x7c000000;
553 colormask[4] = 0x03e00000;
554 colormask[5] = 0x001f0000;
555 bitshift[0] = 7; bitshift2[0] = 0;
556 bitshift[1] = 2; bitshift2[1] = 0;
557 bitshift[2] = 0; bitshift2[2] = 3;
558 bitshift[3] = 23; bitshift2[3] = 0;
559 bitshift[4] = 18; bitshift2[4] = 0;
560 bitshift[5] = 13; bitshift2[5] = 0;
561 } else {
562 colormask[0] = mask[0];
563 colormask[1] = mask[1];
564 colormask[2] = mask[2];
565 colormask[3] = (mask[0] & 0xffff) << 16;
566 colormask[4] = (mask[1] & 0xffff) << 16;
567 colormask[5] = (mask[2] & 0xffff) << 16;
568
569 for (i = 0; i < 3; ++i) {
570 buf = colormask[i];
571
572 bitshift[i] = 0;
573 for (bit = 0; bit < 32; ++bit) {
574 if (buf & 1)
575 break;
576 else
577 ++bitshift[i];
578 buf >>= 1;
579 }
580 bitshift[i+3] = bitshift[i] + 16;
581
582 colorbits[i] = 0;
583 for (; bit < 32; ++bit) {
584 if (buf & 1)
585 ++colorbits[i];
586 else
587 break;
588 buf >>= 1;
589 }
590 if (colorbits[i] > 8) { /* over 8-bit depth */
591 bitshift[i] += (colorbits[i] - 8);
592 bitshift[i+3] = bitshift[i] + 16;
593 bitshift2[i] = bitshift2[i+3] = 0;
594 } else
595 bitshift2[i] = bitshift2[i+3] = 8 - colorbits[i];
596 }
597 }
598
599 if (DEBUG > 1)
600 fprintf(stderr, "loadBMP16: bitfields\n"
601 "\tR: bits = %2d, mask = %08x, shift >>%2d, <<%2d\n"
602 "\t (mask = %08x, shift >>%2d, <<%2d)\n"
603 "\tG: bits = %2d, mask = %08x, shift >>%2d, <<%2d\n"
604 "\t (mask = %08x, shift >>%2d, <<%2d)\n"
605 "\tB: bits = %2d, mask = %08x, shift >>%2d, <<%2d\n"
606 "\t (mask = %08x, shift >>%2d, <<%2d)\n",
607 colorbits[0], colormask[0], bitshift[0], bitshift2[0],
608 colormask[3], bitshift[3], bitshift2[3],
609 colorbits[1], colormask[1], bitshift[1], bitshift2[1],
610 colormask[4], bitshift[4], bitshift2[4],
611 colorbits[2], colormask[2], bitshift[2], bitshift2[2],
612 colormask[5], bitshift[5], bitshift2[5]);
613
614 for (y = h-1; y >= 0 && (pp - pic24 <= l); y--) {
615 pp = pic24 + (3 * w * y);
616 if ((y&0x3f)==0) WaitCursor();
617
618 for (x = w; x > 1; x -= 2) {
619 buf = getint(fp);
620 *(pp++) = (buf & colormask[0]) >> bitshift[0] << bitshift2[0];
621 *(pp++) = (buf & colormask[1]) >> bitshift[1] << bitshift2[1];
622 *(pp++) = (buf & colormask[2]) >> bitshift[2] << bitshift2[2];
623 *(pp++) = (buf & colormask[3]) >> bitshift[3] << bitshift2[3];
624 *(pp++) = (buf & colormask[4]) >> bitshift[4] << bitshift2[4];
625 *(pp++) = (buf & colormask[5]) >> bitshift[5] << bitshift2[5];
626 }
627 if (w & 1) { /* padded to 2 pix */
628 buf = getint(fp);
629 *(pp++) = (buf & colormask[0]) >> bitshift[0];
630 *(pp++) = (buf & colormask[1]) >> bitshift[1];
631 *(pp++) = (buf & colormask[2]) >> bitshift[2];
632 }
633 }
634
635 return FERROR(fp)? 1 : 0;
636 }
637
638
639
640 /*******************************************/
loadBMP24(fp,pic24,w,h,bits)641 static int loadBMP24(fp, pic24, w, h, bits) /* also handles 32-bit BI_RGB */
642 FILE *fp;
643 byte *pic24;
644 u_int w,h, bits;
645 {
646 int i,j,padb,rv;
647 byte *pp = pic24 + ((h - 1) * w * 3);
648 size_t l = w*h*3;
649
650 rv = 0;
651
652 padb = (4 - ((w*3) % 4)) & 0x03; /* # of pad bytes to read at EOscanline */
653 if (bits==32) padb = 0;
654
655 for (i=h-1; i>=0; i--) {
656 pp = pic24 + (i * w * 3);
657 if ((i&0x3f)==0) WaitCursor();
658
659 for (j=0; j<w && (pp - pic24 <= l); j++) {
660 pp[2] = getc(fp); /* blue */
661 pp[1] = getc(fp); /* green */
662 pp[0] = getc(fp); /* red */
663 if (bits==32) getc(fp);
664 pp += 3;
665 }
666
667 for (j=0; j<padb; j++) getc(fp);
668
669 rv = (FERROR(fp));
670 if (rv) break;
671 }
672
673 return rv;
674 }
675
676
677
678 /*******************************************/
loadBMP32(fp,pic24,w,h,colormask)679 static int loadBMP32(fp, pic24, w, h, colormask) /* 32-bit BI_BITFIELDS only */
680 FILE *fp;
681 byte *pic24;
682 u_int w, h, *colormask;
683 {
684 int x, y;
685 byte *pp;
686 u_int buf;
687 int i, bit, bitshift[3], colorbits[3], bitshift2[3];
688
689 for (i = 0; i < 3; ++i) {
690 buf = colormask[i];
691
692 bitshift[i] = 0;
693 for (bit = 0; bit < 32; ++bit) {
694 if (buf & 1)
695 break;
696 else
697 ++bitshift[i];
698 buf >>= 1;
699 }
700
701 colorbits[i] = 0;
702 for (; bit < 32; ++bit) {
703 if (buf & 1)
704 ++colorbits[i];
705 else
706 break;
707 buf >>= 1;
708 }
709 if (colorbits[i] > 8) { /* over 8-bit depth */
710 bitshift[i] += (colorbits[i] - 8);
711 bitshift2[i] = 0;
712 } else
713 bitshift2[i] = 8 - colorbits[i];
714 }
715
716 if (DEBUG > 1)
717 fprintf(stderr, "loadBMP32: bitfields\n"
718 "\tR: bits = %2d, mask = %08x, shift >>%2d, <<%2d\n"
719 "\tG: bits = %2d, mask = %08x, shift >>%2d, <<%2d\n"
720 "\tB: bits = %2d, mask = %08x, shift >>%2d, <<%2d\n",
721 colorbits[0], colormask[0], bitshift[0], bitshift2[0],
722 colorbits[1], colormask[1], bitshift[1], bitshift2[1],
723 colorbits[2], colormask[2], bitshift[2], bitshift2[2]);
724
725 for (y = h-1; y >= 0; y--) {
726 pp = pic24 + (3 * w * y);
727 if ((y&0x3f)==0) WaitCursor();
728
729 for(x = w; x > 0; x --) {
730 buf = getint(fp);
731 *(pp++) = (buf & colormask[0]) >> bitshift[0] << bitshift2[0];
732 *(pp++) = (buf & colormask[1]) >> bitshift[1] << bitshift2[1];
733 *(pp++) = (buf & colormask[2]) >> bitshift[2] << bitshift2[2];
734 }
735 }
736
737 return FERROR(fp)? 1 : 0;
738 }
739
740
741
742 /*******************************************/
getshort(fp)743 static u_int getshort(fp)
744 FILE *fp;
745 {
746 int c, c1;
747 c = getc(fp); c1 = getc(fp);
748 return ((u_int) c) + (((u_int) c1) << 8);
749 }
750
751
752 /*******************************************/
getint(fp)753 static u_int getint(fp)
754 FILE *fp;
755 {
756 int c, c1, c2, c3;
757 c = getc(fp); c1 = getc(fp); c2 = getc(fp); c3 = getc(fp);
758 return ((u_int) c) +
759 (((u_int) c1) << 8) +
760 (((u_int) c2) << 16) +
761 (((u_int) c3) << 24);
762 }
763
764
765 /*******************************************/
putshort(fp,i)766 static void putshort(fp, i)
767 FILE *fp;
768 int i;
769 {
770 int c, c1;
771
772 c = ((u_int) i) & 0xff; c1 = (((u_int) i)>>8) & 0xff;
773 putc(c, fp); putc(c1,fp);
774 }
775
776
777 /*******************************************/
putint(fp,i)778 static void putint(fp, i)
779 FILE *fp;
780 int i;
781 {
782 int c, c1, c2, c3;
783 c = ((u_int) i) & 0xff;
784 c1 = (((u_int) i)>>8) & 0xff;
785 c2 = (((u_int) i)>>16) & 0xff;
786 c3 = (((u_int) i)>>24) & 0xff;
787
788 putc(c, fp); putc(c1,fp); putc(c2,fp); putc(c3,fp);
789 }
790
791
792
793
794 static byte pc2nc[256],r1[256],g1[256],b1[256];
795
796
797 /*******************************************/
WriteBMP(fp,pic824,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle)798 int WriteBMP(fp,pic824,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle)
799 FILE *fp;
800 byte *pic824;
801 int ptype,w,h;
802 byte *rmap, *gmap, *bmap;
803 int numcols, colorstyle;
804 {
805 /*
806 * if PIC8, and colorstyle == F_FULLCOLOR, F_GREYSCALE, or F_REDUCED,
807 * the program writes an uncompressed 4- or 8-bit image (depending on
808 * the value of numcols)
809 *
810 * if PIC24, and colorstyle == F_FULLCOLOR, program writes an uncompressed
811 * 24-bit image
812 * if PIC24 and colorstyle = F_GREYSCALE, program writes an uncompressed
813 * 8-bit image
814 * note that PIC24 and F_BWDITHER/F_REDUCED won't happen
815 *
816 * if colorstyle == F_BWDITHER, it writes a 1-bit image
817 *
818 */
819
820 int i,j, nc, nbits, bperlin, cmaplen, npixels;
821 byte *graypic, *sp, *dp, graymap[256];
822
823 nc = nbits = cmaplen = 0;
824 graypic = NULL;
825
826 if (ptype == PIC24 && colorstyle == F_GREYSCALE) {
827 /* generate a faked 8-bit per pixel image with a grayscale cmap,
828 so that it can just fall through existing 8-bit code */
829
830 npixels = w * h;
831 if (w <= 0 || h <= 0 || npixels/w != h) {
832 SetISTR(ISTR_WARNING, "image dimensions too large");
833 return -1;
834 }
835
836 graypic = (byte *) malloc((size_t) npixels);
837 if (!graypic) FatalError("unable to malloc in WriteBMP()");
838
839 for (i=0,sp=pic824,dp=graypic; i<npixels; i++,sp+=3, dp++) {
840 *dp = MONO(sp[0],sp[1],sp[2]);
841 }
842
843 for (i=0; i<256; i++) graymap[i] = i;
844 rmap = gmap = bmap = graymap;
845 numcols = 256;
846 ptype = PIC8;
847
848 pic824 = graypic;
849 }
850
851
852 if (ptype == PIC24) { /* is F_FULLCOLOR */
853 nbits = 24;
854 cmaplen = 0;
855 nc = 0;
856 }
857
858 else if (ptype == PIC8) {
859 /* we may have duplicate colors in the colormap, and we'd prefer not to.
860 * build r1,g1,b1 (a contiguous, minimum set colormap), and pc2nc[], a
861 * array that maps 'pic8' values (0-numcols) into corresponding values
862 * in the r1,g1,b1 colormaps (0-nc)
863 */
864
865 for (i=0; i<256; i++) { pc2nc[i] = r1[i] = g1[i] = b1[i] = 0; }
866
867 nc = 0;
868 for (i=0; i<numcols; i++) {
869 /* see if color #i is a duplicate */
870 for (j=0; j<i; j++) {
871 if (rmap[i] == rmap[j] && gmap[i] == gmap[j] &&
872 bmap[i] == bmap[j]) break;
873 }
874
875 if (j==i) { /* wasn't found */
876 pc2nc[i] = nc;
877 r1[nc] = rmap[i];
878 g1[nc] = gmap[i];
879 b1[nc] = bmap[i];
880 nc++;
881 }
882 else pc2nc[i] = pc2nc[j];
883 }
884
885 /* determine how many bits per pixel we'll be writing */
886 if (colorstyle == F_BWDITHER || nc <= 2) nbits = 1;
887 else if (nc<=16) nbits = 4;
888 else nbits = 8;
889
890 cmaplen = 1<<nbits; /* # of entries in cmap */
891 }
892
893
894 bperlin = ((w * nbits + 31) / 32) * 4; /* # bytes written per line */
895
896 putc('B', fp); putc('M', fp); /* BMP file magic number */
897
898 /* compute filesize and write it */
899 i = 14 + /* size of bitmap file header */
900 40 + /* size of bitmap info header */
901 (nc * 4) + /* size of colormap */
902 bperlin * h; /* size of image data */
903
904 putint(fp, i);
905 putshort(fp, 0); /* reserved1 */
906 putshort(fp, 0); /* reserved2 */
907 putint(fp, 14 + 40 + (nc * 4)); /* offset from BOfile to BObitmap */
908
909 putint(fp, 40); /* biSize: size of bitmap info header */
910 putint(fp, w); /* biWidth */
911 putint(fp, h); /* biHeight */
912 putshort(fp, 1); /* biPlanes: must be '1' */
913 putshort(fp, nbits); /* biBitCount: 1,4,8, or 24 */
914 putint(fp, BI_RGB); /* biCompression: BI_RGB, BI_RLE8 or BI_RLE4 */
915 putint(fp, bperlin*h); /* biSizeImage: size of raw image data */
916 putint(fp, 75 * 39); /* biXPelsPerMeter: (75dpi * 39" per meter) */
917 putint(fp, 75 * 39); /* biYPelsPerMeter: (75dpi * 39" per meter) */
918 putint(fp, nc); /* biClrUsed: # of colors used in cmap */
919 putint(fp, nc); /* biClrImportant: same as above */
920
921
922 /* write out the colormap */
923 for (i=0; i<nc; i++) {
924 if (colorstyle == F_GREYSCALE) {
925 j = MONO(r1[i],g1[i],b1[i]);
926 putc(j,fp); putc(j,fp); putc(j,fp); putc(0,fp);
927 }
928 else {
929 putc(b1[i],fp);
930 putc(g1[i],fp);
931 putc(r1[i],fp);
932 putc(0,fp);
933 }
934 }
935
936 /* write out the image */
937 if (nbits == 1) writeBMP1 (fp, pic824, w, h);
938 else if (nbits == 4) writeBMP4 (fp, pic824, w, h);
939 else if (nbits == 8) writeBMP8 (fp, pic824, w, h);
940 else if (nbits == 24) writeBMP24(fp, pic824, w, h);
941
942 if (graypic) free(graypic);
943
944 #ifndef VMS
945 if (FERROR(fp)) return -1;
946 #else
947 if (!FERROR(fp)) return -1;
948 #endif
949
950 return 0;
951 }
952
953
954
955
956 /*******************************************/
writeBMP1(fp,pic8,w,h)957 static void writeBMP1(fp, pic8, w, h)
958 FILE *fp;
959 byte *pic8;
960 int w,h;
961 {
962 int i,j,c,bitnum,padw;
963 byte *pp;
964
965 padw = ((w + 31)/32) * 32; /* 'w', padded to be a multiple of 32 */
966
967 for (i=h-1; i>=0; i--) {
968 pp = pic8 + (i * w);
969 if ((i&0x3f)==0) WaitCursor();
970
971 for (j=bitnum=c=0; j<=padw; j++,bitnum++) {
972 if (bitnum == 8) { /* write the next byte */
973 putc(c,fp);
974 bitnum = c = 0;
975 }
976
977 c <<= 1;
978
979 if (j<w) {
980 c |= (pc2nc[*pp++] & 0x01);
981 }
982 }
983 }
984 }
985
986
987
988 /*******************************************/
writeBMP4(fp,pic8,w,h)989 static void writeBMP4(fp, pic8, w, h)
990 FILE *fp;
991 byte *pic8;
992 int w,h;
993 {
994 int i,j,c,nybnum,padw;
995 byte *pp;
996
997
998 padw = ((w + 7)/8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */
999
1000 for (i=h-1; i>=0; i--) {
1001 pp = pic8 + (i * w);
1002 if ((i&0x3f)==0) WaitCursor();
1003
1004 for (j=nybnum=c=0; j<=padw; j++,nybnum++) {
1005 if (nybnum == 2) { /* write next byte */
1006 putc((c&0xff), fp);
1007 nybnum = c = 0;
1008 }
1009
1010 c <<= 4;
1011
1012 if (j<w) {
1013 c |= (pc2nc[*pp] & 0x0f);
1014 pp++;
1015 }
1016 }
1017 }
1018 }
1019
1020
1021
1022 /*******************************************/
writeBMP8(fp,pic8,w,h)1023 static void writeBMP8(fp, pic8, w, h)
1024 FILE *fp;
1025 byte *pic8;
1026 int w,h;
1027 {
1028 int i,j,padw;
1029 byte *pp;
1030
1031 padw = ((w + 3)/4) * 4; /* 'w' padded to a multiple of 4pix (32 bits) */
1032
1033 for (i=h-1; i>=0; i--) {
1034 pp = pic8 + (i * w);
1035 if ((i&0x3f)==0) WaitCursor();
1036
1037 for (j=0; j<w; j++) putc(pc2nc[*pp++], fp);
1038 for ( ; j<padw; j++) putc(0, fp);
1039 }
1040 }
1041
1042
1043 /*******************************************/
writeBMP24(fp,pic24,w,h)1044 static void writeBMP24(fp, pic24, w, h)
1045 FILE *fp;
1046 byte *pic24;
1047 int w,h;
1048 {
1049 int i,j,padb;
1050 byte *pp;
1051
1052 padb = (4 - ((w*3) % 4)) & 0x03; /* # of pad bytes to write at EOscanline */
1053
1054 for (i=h-1; i>=0; i--) {
1055 pp = pic24 + (i * w * 3);
1056 if ((i&0x3f)==0) WaitCursor();
1057
1058 for (j=0; j<w; j++) {
1059 putc(pp[2], fp);
1060 putc(pp[1], fp);
1061 putc(pp[0], fp);
1062 pp += 3;
1063 }
1064
1065 for (j=0; j<padb; j++) putc(0, fp);
1066 }
1067 }
1068
1069
1070
1071
1072
1073
1074 /*******************************************/
bmpError(fname,st)1075 static int bmpError(fname, st)
1076 const char *fname, *st;
1077 {
1078 SetISTR(ISTR_WARNING,"%s: %s", fname, st);
1079 return 0;
1080 }
1081
1082
1083
1084