1 /*
2
3 Copyright Andy Key, Heiko Nitzsche
4
5 gbmbmp.c - OS/2 1.1, 1.2, 2.0 and Windows 3.0 support
6
7 Reads and writes any OS/2 1.x bitmap.
8 Will also read uncompressed, RLE4 and RLE8 Windows 3.x bitmaps too.
9 There are horrific file structure alignment considerations hence each
10 word,dword is read individually.
11
12 */
13
14 #include "img.h"
15 #include "Image.h"
16
17 static char * bmpext[] = {
18 "bmp", "vga", "bga",
19 "rle", "dib", "rl4",
20 "rl8", NULL,
21 };
22
23 static int bmpbpp[] = {
24 imRGB,
25 imbpp8 | imGrayScale, imbpp8,
26 imbpp4 | imGrayScale, imbpp4,
27 imbpp1 | imGrayScale, imbpp1,
28 0
29 };
30 static char * bmpfeat[] = {
31 "OS/2 BMP 1.0",
32 "Windows BMP 1.0",
33 "Windows BMP 2.0",
34 "Windows BMP 3.0",
35 "Windows BMP 3.0 RLE4",
36 "Windows BMP 3.0 RLE8",
37 NULL
38 };
39
40 static char * loadOutput[] = {
41 "HotSpotX",
42 "HotSpotY",
43 "OS2",
44 "Compression",
45 "ImportantColors",
46 "XResolution",
47 "YResolution",
48 "BitDepth",
49 NULL
50 };
51
52 static char * mime[] = {
53 "image/bmp",
54 "image/x-bmp",
55 "image/x-MS-bmp",
56 "image/x-win-bitmap",
57 NULL
58 };
59
60 static ImgCodecInfo codec_info = {
61 "Windows Bitmap",
62 "GBM by Andy Key",
63 1, 7, /* version */
64 bmpext, /* extension */
65 "Windows Bitmap", /* file type */
66 "BMP", /* short type */
67 bmpfeat, /* features */
68 "", /* module */
69 "", /* package */
70 IMG_LOAD_FROM_FILE | IMG_LOAD_MULTIFRAME | IMG_LOAD_FROM_STREAM |
71 IMG_SAVE_TO_FILE | IMG_SAVE_TO_STREAM,
72 bmpbpp, /* save types */
73 loadOutput,
74 mime
75 };
76
77 static void *
init(PImgCodecInfo * info,void * param)78 init( PImgCodecInfo * info, void * param)
79 {
80 *info = &codec_info;
81 return (void*)1;
82 }
83
84 #ifndef min
85 #define min(a,b) (((a)<(b))?(a):(b))
86 #endif
87
88 typedef uint32_t dword;
89 typedef uint16_t word;
90 typedef uint8_t byte;
91
92 #define low_byte(w) ((byte) ((w)&0x00ff) )
93 #define high_byte(w) ((byte) (((w)&0xff00)>>8))
94 #define make_word(a,b) (((word)a) + (((word)b) << 8))
95
96 static Bool
read_word(PImgIORequest req,word * w)97 read_word( PImgIORequest req, word *w)
98 {
99 Byte low = 0, high = 0;
100
101 if ( req_read(req, 1, &low) != 1 )
102 return false;
103 if ( req_read(req, 1, &high) != 1 )
104 return false;
105 *w = (word) (low + ((word) high << 8));
106 return true;
107 }
108
109 static Bool
read_dword(PImgIORequest fd,dword * d)110 read_dword(PImgIORequest fd, dword *d)
111 {
112 word low, high;
113 if ( !read_word(fd, &low) )
114 return false;
115 if ( !read_word(fd, &high) )
116 return false;
117 *d = low + ((dword) high << 16);
118 return true;
119 }
120
121
122 static Bool
write_word(PImgIORequest fd,word w)123 write_word(PImgIORequest fd, word w)
124 {
125 byte low = (byte) (w & 0xFF);
126 byte high = (byte) (w >> 8);
127
128 req_write( fd, 1, &low);
129 req_write( fd, 1, &high);
130
131 return true;
132 }
133
134 static Bool
write_dword(PImgIORequest req,dword d)135 write_dword(PImgIORequest req, dword d)
136 {
137 write_word(req, (word) (d & 0xFFFF));
138 write_word(req, (word) (d >> 16));
139 return true;
140 }
141
142 #define BFT_BMAP 0x4d42
143 #define BFT_BITMAPARRAY 0x4142
144 #define BCA_UNCOMP 0x00000000L
145 #define BCA_RLE8 0x00000001L
146 #define BCA_RLE4 0x00000002L
147 #define BCA_BITFIELDS 0x00000003L
148 #define BCA_RLE24 0x00000004L
149 #define BCA_MAX BCA_RLE24
150 #define MSWCC_EOL 0
151 #define MSWCC_EOB 1
152 #define MSWCC_DELTA 2
153
154 static char * bca_sets[] = {
155 "Uncompressed",
156 "RLE8",
157 "RLE4",
158 "Raw bits",
159 "RLE24"
160 };
161
162 static void
swap_pal(RGBColor * p)163 swap_pal( RGBColor * p)
164 {
165 RGBColor tmp = p[0];
166 p[0] = p[1];
167 p[1] = tmp;
168 }
169
170 #define outc(x){ strncpy( fi-> errbuf, x, 256); return false;}
171 #define outr(fd) { snprintf( fi-> errbuf, 256, "Read error:%s",strerror( req_error( fd))); return false; }
172 #define outw(fd) { snprintf( fi-> errbuf, 256, "Write error:%s",strerror( req_error( fd))); return false; }
173 #define outs(fd) { snprintf( fi-> errbuf, 256, "Seek error:%s",strerror( req_error( fd))); return false; }
174 #define outcm(dd){ snprintf( fi-> errbuf, 256, "Not enough memory (%d bytes)", (int)(dd)); return false;}
175 #define outcd(x,dd){ snprintf( fi-> errbuf, 256, x, (int)(dd)); return false;}
176
177
178 #define BUFSIZE 16384
179
180 typedef struct {
181 byte buf[BUFSIZE];
182 int inx, cnt, y, lasty;
183 unsigned long ls, read;
184 PImgLoadFileInstance fi;
185 Bool error;
186 } AHEAD;
187
188 static AHEAD *
create_ahead(PImgLoadFileInstance fi,unsigned long lineSize)189 create_ahead(PImgLoadFileInstance fi, unsigned long lineSize)
190 {
191 AHEAD *ahead;
192
193 if ( (ahead = malloc((size_t) sizeof(AHEAD))) == NULL )
194 return NULL;
195
196 ahead-> inx = 0;
197 ahead-> cnt = 0;
198 ahead-> fi = fi;
199 ahead-> error = false;
200 ahead-> y = 0;
201 ahead-> lasty = 0;
202 ahead-> read = 0;
203 ahead-> ls = lineSize;
204
205 return ahead;
206 }
207
208 /* returns true on hard failure */
209 static Bool
destroy_ahead(AHEAD * ahead)210 destroy_ahead(AHEAD *ahead)
211 {
212 Bool error = ahead-> error;
213 free(ahead);
214 if (error & ahead-> fi-> wasTruncated) error = false;
215 return error;
216 }
217
218 static byte
read_ahead(AHEAD * ahead)219 read_ahead(AHEAD *ahead)
220 {
221 if ( ahead-> inx >= ahead-> cnt ) {
222 if ( ahead-> error) return 0;
223
224 ahead-> cnt = req_read( ahead->fi->req, BUFSIZE, ahead->buf);
225 if ( ahead-> cnt <= 0 ) {
226 snprintf( ahead->fi-> errbuf, 256,
227 "Read error:%s",
228 (( ahead-> cnt == 0) ?
229 "unexpected end of file" :
230 strerror( req_error( ahead-> fi-> req))
231 ));
232 ahead-> error = 1;
233 if ( !ahead-> fi-> noIncomplete && ahead-> cnt >= 0)
234 ahead-> fi-> wasTruncated = true;
235 return 0;
236 }
237
238 ahead-> read += ahead-> cnt;
239 ahead-> lasty = ahead-> y;
240 ahead-> y = ahead-> read / ahead-> ls;
241 ahead-> inx = 0;
242 EVENT_SCANLINES_READY(ahead-> fi, ahead-> y - ahead-> lasty, SCANLINES_DIR_BOTTOM_TO_TOP);
243 }
244
245 return ahead->buf[ahead->inx++];
246 }
247
248 typedef struct _RGBTriplet {
249 dword r;
250 dword g;
251 dword b;
252 } RGBTriplet;
253
254 typedef struct _LoadRec {
255 long base;
256 Bool windows;
257 dword cbFix;
258 dword ulCompression;
259 dword cclrUsed;
260 dword cclrImportant;
261 dword offBits;
262 word xHotspot;
263 word yHotspot;
264 Point resolution;
265 Bool multiframe;
266 int w, h, bpp;
267 int passed;
268 size_t passed_frame_offset;
269 size_t file_start_offset;
270 RGBTriplet rgb_offset;
271 RGBTriplet rgb_mask;
272 RGBTriplet rgb_valid_bits;
273 } LoadRec;
274
275
276 static void *
open_load(PImgCodec instance,PImgLoadFileInstance fi)277 open_load( PImgCodec instance, PImgLoadFileInstance fi)
278 {
279 LoadRec * l;
280 PImgIORequest fd;
281 word usType;
282
283 if ( req_seek( fi-> req, 0, SEEK_SET) < 0) return NULL;
284
285 fd = fi-> req;
286 if ( !read_word( fd, &usType)) outr(fd);
287
288 if ( usType != BFT_BMAP && usType != BFT_BITMAPARRAY)
289 return NULL;
290
291 fi-> stop = true;
292 l = malloc( sizeof( LoadRec));
293 if ( !l) outcm( sizeof( LoadRec));
294 memset( l, 0, sizeof( LoadRec));
295 fi-> instance = l;
296
297 l-> multiframe = usType == BFT_BITMAPARRAY;
298 l-> passed = -1;
299 l-> file_start_offset = l-> passed_frame_offset = req_tell( fi-> req);
300 if ( !l-> multiframe) fi-> frameCount = 1;
301
302 return l;
303 }
304
305 static Bool
rewind_to_frame(PImgLoadFileInstance fi)306 rewind_to_frame( PImgLoadFileInstance fi)
307 {
308 LoadRec * l = ( LoadRec *) fi-> instance;
309 PImgIORequest fd = fi-> req;
310 word usType;
311 dword cbSize2, offNext, dummy;
312
313 if ( !l-> multiframe)
314 return true;
315
316 if ( l-> passed >= fi-> frame) {
317 /* reset to first frame */
318 l-> passed = -1;
319 l-> passed_frame_offset = l-> file_start_offset;
320 }
321
322 if ( req_seek( fd, l-> passed_frame_offset, SEEK_CUR) < 0)
323 outs(fd);
324
325 while ( ++l-> passed < fi-> frame ) {
326 if ( !read_dword( fd, &cbSize2) )
327 outr(fd);
328 if ( !read_dword( fd, &offNext) )
329 outr(fd);
330 if ( offNext == 0L ) {
331 fi-> frameCount = l-> passed;
332 snprintf( fi-> errbuf, 256, "Index %d is out of range", fi-> frame);
333 return false;
334 }
335 if ( req_seek( fd, (long )offNext, SEEK_SET) < 0)
336 outs(fd);
337 if ( !read_word( fi-> req, &usType) )
338 outr(fd);
339 if ( usType != BFT_BITMAPARRAY ) {
340 snprintf( fi-> errbuf, 256, "Bad array magic at index %d", l-> passed);
341 return false;
342 }
343 }
344 if ( !read_dword( fd, &cbSize2) )
345 outr(fd);
346 if ( !read_dword( fd, &offNext) )
347 outr(fd);
348 if ( !read_dword( fd, &dummy) )
349 outr(fd);
350 if ( !read_word(fd, &usType) )
351 outr(fd);
352 if ( usType != BFT_BMAP ) {
353 snprintf( fi-> errbuf, 256, "Bad magic at index %d", l-> passed);
354 return false;
355 }
356 l-> passed_frame_offset = offNext;
357
358 return true;
359 }
360
361 static dword
count_mask_bits(dword mask,dword * bitoffset)362 count_mask_bits(dword mask, dword * bitoffset)
363 {
364 dword testmask = 1; /* start with the least significant bit */
365 dword counter = 0;
366 dword index = 0;
367
368 /* find offset of first bit */
369 while (((mask & testmask) == 0) && (index < 31)) {
370 index++;
371 testmask <<= 1;
372 }
373 *bitoffset = index;
374
375 /* count the bits set in the rest of the mask */
376 while ((testmask <= mask) && (index < 31)) {
377 if (mask & testmask) {
378 counter++;
379 }
380 index++;
381 testmask <<= 1;
382 }
383
384 return counter;
385 }
386
387
388 static Bool
read_bmp_header(PImgLoadFileInstance fi)389 read_bmp_header( PImgLoadFileInstance fi)
390 {
391 LoadRec * l = ( LoadRec *) fi-> instance;
392 PImgIORequest fd = fi-> req;
393 dword cbSize, cbFix;
394
395 l-> base = req_tell(fd) - 2L; /* BM */
396 if ( l-> base < 0)
397 outs(fd);
398 if ( !read_dword(fd, &cbSize) )
399 outr(fd);
400 if ( !read_word(fd, &l-> xHotspot) )
401 outr(fd);
402 if ( !read_word(fd, &l-> yHotspot) )
403 outr(fd);
404 if ( !read_dword(fd, &l-> offBits) )
405 outr(fd);
406 if ( !read_dword(fd, &cbFix) )
407 outr(fd);
408
409 if ( cbFix == 12 ) {
410 /* OS/2 1.x uncompressed bitmap */
411 word cx, cy, cPlanes, cBitCount;
412
413 if ( !read_word(fd, &cx) )
414 outr(fd);
415 if ( !read_word(fd, &cy) )
416 outr(fd);
417 if ( !read_word(fd, &cPlanes) )
418 outr(fd);
419 if ( !read_word(fd, &cBitCount) )
420 outr(fd);
421
422 if ( cx == 0 || cy == 0 )
423 outc("Bad size");
424 if ( cPlanes != 1 )
425 outcd("Number of bitmap planes is %d, must be 1", cPlanes);
426 if ( cBitCount != 1 && cBitCount != 4 && cBitCount != 8 && cBitCount != 24 )
427 outcd("Bit count is %d, must be 1, 4, 8 or 24", cBitCount);
428
429 l-> w = (int) cx;
430 l-> h = (int) cy;
431 l-> bpp = (int) cBitCount;
432 l-> windows = false;
433 }
434 else if (
435 cbFix >= 16 && cbFix <= 64 &&
436 ((cbFix & 3) == 0 || cbFix == 42 || cbFix == 46)
437 ) {
438 /* OS/2 and Windows 3 */
439 word cPlanes = 0, cBitCount = 0, usUnits = 0, usReserved, usRecording, usRendering = 0;
440 dword ulWidth = 0, ulHeight = 0, ulCompression = 0;
441 dword ulSizeImage, ulXPelsPerMeter = 0, ulYPelsPerMeter = 0;
442 dword cclrUsed = 0, cclrImportant = 0, cSize1, cSize2, ulColorEncoding, ulIdentifier;
443 Bool ok;
444
445 ok = read_dword(fd, &ulWidth);
446 ok &= read_dword(fd, &ulHeight);
447 ok &= read_word(fd, &cPlanes);
448 ok &= read_word(fd, &cBitCount);
449 if ( cbFix > 16 )
450 ok &= read_dword(fd, &ulCompression);
451 else
452 ulCompression = BCA_UNCOMP;
453
454 if ( cbFix > 20 )
455 ok &= read_dword(fd, &ulSizeImage);
456 if ( cbFix > 24 )
457 ok &= read_dword(fd, &ulXPelsPerMeter);
458 if ( cbFix > 28 )
459 ok &= read_dword(fd, &ulYPelsPerMeter);
460 if ( cbFix > 32 )
461 ok &= read_dword(fd, &cclrUsed);
462 else
463 cclrUsed = ( (dword)1 << cBitCount );
464 if ( cBitCount != 24 && cclrUsed == 0 )
465 cclrUsed = ( (dword)1 << cBitCount );
466
467 /* Protect against badly written bitmaps! */
468 if ( cclrUsed > ( (dword)1 << cBitCount ) )
469 cclrUsed = ( (dword)1 << cBitCount );
470
471 if ( cbFix > 36 )
472 ok &= read_dword(fd, &cclrImportant);
473 if ( cbFix > 40 )
474 ok &= read_word(fd, &usUnits);
475 if ( cbFix > 42 )
476 ok &= read_word(fd, &usReserved);
477 if ( cbFix > 44 )
478 ok &= read_word(fd, &usRecording);
479 if ( cbFix > 46 )
480 ok &= read_word(fd, &usRendering);
481 if ( cbFix > 48 )
482 ok &= read_dword(fd, &cSize1);
483 if ( cbFix > 52 )
484 ok &= read_dword(fd, &cSize2);
485 if ( cbFix > 56 )
486 ok &= read_dword(fd, &ulColorEncoding);
487 if ( cbFix > 60 )
488 ok &= read_dword(fd, &ulIdentifier);
489
490 if ( !ok )
491 outr(fd);
492
493 if ( ulWidth == 0L || ulHeight == 0L )
494 outc("Bad image size");
495 if ( cPlanes != 1 )
496 outcd("Number of bitmap planes is %d, must be 1", cPlanes);
497 if (
498 cBitCount != 1 &&
499 cBitCount != 4 &&
500 cBitCount != 8 &&
501 cBitCount != 16 &&
502 cBitCount != 24 &&
503 cBitCount != 32
504 )
505 outcd("Bit count is %d, must be 1, 4, 8, 16, 24 or 32", cBitCount);
506
507 l-> w = (int) ulWidth;
508 l-> h = (int) ulHeight;
509 l-> bpp = (int) cBitCount;
510 l-> windows = true;
511 l-> cbFix = cbFix;
512 l-> ulCompression = ulCompression;
513 l-> cclrUsed = cclrUsed;
514 l-> cclrImportant = cclrImportant;
515 l-> resolution.x = ulXPelsPerMeter;
516 l-> resolution.y = ulYPelsPerMeter;
517 } else
518 outc("cbFix is bad");
519
520 if ( l-> bpp == 16 || l-> bpp == 32 ) {
521 switch ( l-> ulCompression) {
522 case BCA_UNCOMP:
523 l-> rgb_offset. b = 0;
524 l-> rgb_offset. g = (l->bpp == 16) ? 5 : 8;
525 l-> rgb_offset. r = (l->bpp == 16) ? 10 : 16;
526
527 /* set color masks to either 16bpp (5,5,5) or 32bpp (8,8,8) */
528 l-> rgb_mask. b = (l-> bpp == 16) ? 0x001f : 0x000000ff;
529 l-> rgb_mask. g = (l-> bpp == 16) ? 0x03e0 : 0x0000ff00;
530 l-> rgb_mask. r = (l-> bpp == 16) ? 0x7c00 : 0x00ff0000;
531
532 l-> rgb_valid_bits. b =
533 l-> rgb_valid_bits. g =
534 l-> rgb_valid_bits. r = (l->bpp == 16) ? 5 : 8;
535 break;
536
537 case BCA_BITFIELDS: {
538 Bool ok = 1;
539
540 /* Read BI_BITFIELDS color masks from the header (where usually the palette is) */
541 /* These are strangely stored as dwords in the order of R-G-B. */
542 if ( req_seek(fd, (long) (l-> base + 14L + l-> cbFix), SEEK_SET) < 0)
543 outs(fd);
544 ok &= read_dword(fd, &l-> rgb_mask. r);
545 ok &= read_dword(fd, &l-> rgb_mask. g);
546 ok &= read_dword(fd, &l-> rgb_mask. b);
547 if ( !ok )
548 outr(fd);
549
550 /* count the bits used in each mask */
551 l-> rgb_valid_bits. b = count_mask_bits( l-> rgb_mask. b, &l-> rgb_offset. b);
552 l-> rgb_valid_bits. g = count_mask_bits( l-> rgb_mask. g, &l-> rgb_offset. g);
553 l-> rgb_valid_bits. r = count_mask_bits( l-> rgb_mask. r, &l-> rgb_offset. r);
554
555 /* Only up to 8 bit per mask are allowed */
556 if (
557 l-> rgb_valid_bits. b > 8 ||
558 l-> rgb_valid_bits. g > 8 ||
559 l-> rgb_valid_bits. r > 8
560 )
561 outc("Bad bit masks for non-24bits RGB data");
562
563 /* check for non-overlapping bits */
564 if (
565 l-> rgb_valid_bits. b +
566 l-> rgb_valid_bits. g +
567 l-> rgb_valid_bits. r > l-> bpp
568 )
569 outc("Bad bit masks for non-24bits RGB data");
570
571
572 if (
573 l-> rgb_offset. b + l-> rgb_valid_bits. b > l-> rgb_offset. g ||
574 l-> rgb_offset. g + l-> rgb_valid_bits. g > l-> rgb_offset. r ||
575 l-> rgb_offset. r + l-> rgb_valid_bits. r > l-> bpp
576 )
577 outc("Bad bit masks for non-24bits RGB data");
578
579 l-> rgb_valid_bits. r = 8 - l-> rgb_valid_bits. r;
580 l-> rgb_valid_bits. g = 8 - l-> rgb_valid_bits. g;
581 l-> rgb_valid_bits. b = 8 - l-> rgb_valid_bits. b;
582 break;
583 }
584
585 default:
586 outcd("compression type is %d, expected 0 or 3", l->ulCompression);
587 }}
588 return true;
589 }
590
591 static Bool
req_read_big(PImgLoadFileInstance fi,int h,unsigned long lineSize,Byte * data)592 req_read_big( PImgLoadFileInstance fi, int h, unsigned long lineSize, Byte * data)
593 {
594 unsigned long size = h * lineSize, read = 0;
595 int lasty = 0, y = 0;
596
597 if ( fi-> eventMask & IMG_EVENTS_DATA_READY) {
598 /* read and notify */
599 while ( size > 0) {
600 ssize_t r = req_read(
601 fi-> req,
602 ( BUFSIZE > size) ? size : BUFSIZE,
603 data
604 );
605 if ( r < 0)
606 outr( fi-> req);
607 if ( r == 0) {
608 if ( fi-> noIncomplete)
609 outc("Read error: unexpected end of file")
610 else
611 size = 0;
612 }
613 read += r;
614 size -= r;
615 data += r;
616 lasty = y;
617 y = read / lineSize;
618 EVENT_SCANLINES_READY(fi, y - lasty, SCANLINES_DIR_BOTTOM_TO_TOP);
619 }
620 } else {
621 /* just read */
622 ssize_t r = req_read( fi-> req, size, data);
623 if ( r < 0)
624 outr( fi-> req);
625 if ( r != size && fi-> noIncomplete)
626 outc( "Read error: unexpected end of file");
627 }
628
629 return true;
630 }
631
632 /* Read 16bpp data with compression BI_RGB or BI_BITFIELDS (will be mapped to 24bpp, lossless) */
633 static Bool
read_16_32_bpp(PImgLoadFileInstance fi,PImage i,int bpp,unsigned long stride_dst)634 read_16_32_bpp( PImgLoadFileInstance fi, PImage i, int bpp, unsigned long stride_dst)
635 {
636 LoadRec * l = ( LoadRec *) fi-> instance;
637 int h, stride_src = ((i-> w * 16 + 31) / 32) * 4;
638 Byte *src, *dst;
639
640 if ( !( src = (Byte*) malloc(stride_src)))
641 outcm(stride_src);
642
643 dst = i-> data; /* write pointer */
644 for (h = 0; h < i-> h; h++) {
645 int block_count = i-> w;
646 const word * src16 = (const word *) src;
647 const dword * src32 = (const dword *) src;
648 Byte * line = dst;
649 ssize_t r = req_read( fi-> req, stride_src, src);
650
651 if ( r != stride_src ) {
652 free( src);
653 if ( r < 0)
654 outr( fi-> req);
655 if ( fi-> noIncomplete)
656 outc("Read error: unexpected end of file");
657 h = i->h;
658 fi-> wasTruncated = true;
659 }
660
661 /* Extract red, green, blue. */
662 /* Encoding starts at least significant bit, then xB,yG,zR (most significant bit is unused). */
663 /* Map these into 24bpp BGR. */
664 #define GetX(X,SRC) (((SRC & l-> rgb_mask.X) >> l->rgb_offset.X) << l->rgb_valid_bits.X)
665 if ( bpp == 16 ) {
666 while (block_count > 0) {
667 register word data16 = *src16++;
668 *line++ = GetX(b,data16);
669 *line++ = GetX(g,data16);
670 *line++ = GetX(r,data16);
671 --block_count;
672 }
673 } else {
674 while (block_count > 0) {
675 register dword data32 = *src32++;
676 *line++ = GetX(b,data32);
677 *line++ = GetX(g,data32);
678 *line++ = GetX(r,data32);
679 --block_count;
680 }
681 }
682 #undef GetX
683 dst += stride_dst;
684 EVENT_SCANLINES_READY(fi, 1, SCANLINES_DIR_BOTTOM_TO_TOP);
685 }
686
687 free(src);
688 return true;
689 }
690
691
692 static Bool
load(PImgCodec instance,PImgLoadFileInstance fi)693 load( PImgCodec instance, PImgLoadFileInstance fi)
694 {
695 HV * profile = fi-> frameProperties;
696 LoadRec * l = ( LoadRec *) fi-> instance;
697 PImgIORequest fd = fi-> req;
698 PImage img;
699 int cLinesWorth; /* bmp alignment and prima alignment are identical, by 4-byte boundary */
700 int bpp;
701 Byte * data;
702
703 if ( !rewind_to_frame(fi))
704 return false;
705 if ( !read_bmp_header(fi))
706 return false;
707
708 img = PImage( fi-> object);
709 bpp = ( l-> bpp == 16 || l-> bpp == 32 ) ? 24 : l-> bpp;
710 if ( fi-> noImageData) {
711 pset_i( width, l-> w);
712 pset_i( height, l-> h);
713 CImage( fi-> object)-> create_empty( fi-> object, 1, 1, bpp);
714 } else {
715 CImage( fi-> object)-> create_empty( fi-> object, l-> w, l-> h, bpp);
716 EVENT_HEADER_READY( fi);
717 }
718 data = img-> data;
719
720 /* read palette */
721 if ( bpp != 24) {
722 int i;
723 byte b[4];
724 PRGBColor pal;
725
726 pal = img-> palette;
727 if ( l-> windows ) { /* Windows */
728 if ( req_seek(fd, (long) (l-> base + 14L + l-> cbFix), SEEK_SET) < 0)
729 outs(fd);
730 img-> palSize = (int) l-> cclrUsed;
731 for ( i = 0; i < (int) l-> cclrUsed; i++, pal++ ) {
732 if ( req_read( fd, 4, b) != 4)
733 outr(fd);
734 pal-> b = b[0];
735 pal-> g = b[1];
736 pal-> r = b[2];
737 }
738 } else { /* OS/2 */
739 if ( req_seek(fd, (long) (l-> base + 26L), SEEK_SET) < 0)
740 outs(fd);
741 img-> palSize = 1 << l-> bpp;
742 for ( i = 0; i < img-> palSize; i++, pal++ ) {
743 if ( req_read( fd, 3, b) != 3)
744 outr(fd);
745 pal-> b = b[0];
746 pal-> g = b[1];
747 pal-> r = b[2];
748 }
749 }
750 } else {
751 img-> palSize = 0;
752 }
753
754 if ( bpp == 1)
755 swap_pal( img-> palette);
756
757 if ( fi-> loadExtras) {
758 char * c;
759 if ( !l-> windows)
760 pset_i( OS2, 1);
761 pset_i( HotSpotX, l-> xHotspot);
762 pset_i( HotSpotY, l-> yHotspot);
763 pset_i( BitDepth, l-> bpp);
764
765 c = ( l-> ulCompression > BCA_MAX) ?
766 "Unknown" : bca_sets[ l-> ulCompression];
767 pset_c( Compression, c);
768 if ( l-> windows) {
769 pset_i( ImportantColors, l-> cclrImportant);
770 pset_i( XResolution, l-> resolution.x);
771 pset_i( YResolution, l-> resolution.y);
772 }
773 }
774
775 if ( fi-> noImageData)
776 return true;
777
778 /* read data */
779 cLinesWorth = ((bpp * l->w + 31) / 32) * 4;
780
781 if ( l-> windows ) {
782 if ( req_seek( fd, (long) l-> offBits, SEEK_SET) < 0)
783 outs(fd);
784
785 switch ( (int) l-> ulCompression ) {
786
787 case BCA_UNCOMP:
788 switch ( l-> bpp) {
789 case 1:
790 case 4:
791 case 8:
792 case 24:
793 if ( !req_read_big(fi, l-> h, cLinesWorth, data))
794 return false;
795 break;
796 case 16:
797 if ( !read_16_32_bpp(fi, PImage(fi-> object), 16, cLinesWorth))
798 return false;
799 break;
800 case 32:
801 if ( !read_16_32_bpp(fi, PImage(fi-> object), 32, cLinesWorth))
802 return false;
803 break;
804 default:
805 outc("Unsupported bit depth");
806 }
807 break;
808
809 case BCA_RLE8: {
810 AHEAD *ahead;
811 int x = 0, y = 0;
812 Bool eof8 = false;
813
814 if ( (ahead = create_ahead(fi, cLinesWorth)) == NULL )
815 outcm(sizeof(AHEAD));
816
817 while ( !eof8 ) {
818 byte c = read_ahead(ahead);
819 byte d = read_ahead(ahead);
820
821 if ( c ) {
822 memset(data, d, c);
823 x += c;
824 data += c;
825 } else switch ( d ) {
826 case MSWCC_EOL: {
827 int to_eol = cLinesWorth - x;
828
829 memset( data, 0, (size_t) to_eol);
830 data += to_eol;
831 x = 0;
832 if ( ++y == l->h )
833 eof8 = true;
834 }
835 break;
836 case MSWCC_EOB:
837 if ( y < l->h ) {
838 int to_eol = cLinesWorth - x;
839
840 memset(data, 0, (size_t) to_eol);
841 x = 0; y++;
842 data += to_eol;
843 while ( y < l->h ) {
844 memset(data, 0, (size_t) cLinesWorth);
845 data += cLinesWorth;
846 y++;
847 }
848 }
849 eof8 = true;
850 break;
851 case MSWCC_DELTA: {
852 byte dx = read_ahead(ahead);
853 byte dy = read_ahead(ahead);
854 int fill = dx + dy * cLinesWorth;
855 memset(data, 0, (size_t) fill);
856 data += fill;
857 x += dx; y += dy;
858 if ( y == l->h )
859 eof8 = true;
860 }
861 break;
862
863 default: {
864 int n = (int) d;
865
866 while ( n-- > 0 )
867 *data++ = read_ahead(ahead);
868 x += d;
869 if ( d & 1 )
870 read_ahead(ahead); /* Align */
871 }
872 break;
873 }
874 }
875 if ( destroy_ahead(ahead))
876 return false;
877 }
878 break;
879
880 case BCA_RLE4: {
881 AHEAD *ahead;
882 int x = 0, y = 0;
883 Bool eof4 = false;
884 int inx = 0;
885
886 if ( (ahead = create_ahead(fi, cLinesWorth)) == NULL )
887 outcm(sizeof(AHEAD));
888
889 memset(data, 0, l->h * cLinesWorth);
890
891 while ( !eof4 ) {
892 byte c = read_ahead(ahead);
893 byte d = read_ahead(ahead);
894
895 if ( c ) {
896 byte h, l;
897 int i;
898 if ( x & 1 ) {
899 h = (byte) (d >> 4);
900 l = (byte) (d << 4);
901 } else {
902 h = (byte) (d&0xf0);
903 l = (byte) (d & 0x0f);
904 }
905 for ( i = 0; i < (int) c; i++, x++ ) {
906 if ( x & 1U )
907 data[inx++] |= l;
908 else
909 data[inx] |= h;
910 }
911 } else switch ( d ) {
912 case MSWCC_EOL:
913 x = 0;
914 if ( ++y == l->h )
915 eof4 = true;
916 inx = cLinesWorth * y;
917 break;
918
919 case MSWCC_EOB:
920 eof4 = true;
921 break;
922
923 case MSWCC_DELTA: {
924 byte dx = read_ahead(ahead);
925 byte dy = read_ahead(ahead);
926
927 x += dx; y += dy;
928 inx = y * cLinesWorth + (x/2);
929
930 if ( y == l->h )
931 eof4 = true;
932 }
933 break;
934
935 default: {
936 int i, nr = 0;
937
938 if ( x & 1 ) {
939 for ( i = 0; i+2 <= (int) d; i += 2 ) {
940 byte b = read_ahead(ahead);
941 data[inx++] |= (b >> 4);
942 data[inx ] |= (b << 4);
943 nr++;
944 }
945 if ( i < (int) d ) {
946 data[inx++] |= (read_ahead(ahead) >> 4);
947 nr++;
948 }
949 } else {
950 for ( i = 0; i+2 <= (int) d; i += 2 ) {
951 data[inx++] = read_ahead(ahead);
952 nr++;
953 }
954 if ( i < (int) d ) {
955 data[inx] = read_ahead(ahead);
956 nr++;
957 }
958 }
959 x += d;
960
961 if ( nr & 1 )
962 read_ahead(ahead); /* Align input stream to next word */
963 }
964 break;
965 }
966 }
967
968 if ( destroy_ahead(ahead))
969 return false;
970 }
971 break;
972 case BCA_BITFIELDS:
973 switch ( l-> bpp) {
974 case 16:
975 if ( !read_16_32_bpp(fi, PImage(fi-> object), 16, cLinesWorth))
976 return false;
977 break;
978 case 32:
979 if ( !read_16_32_bpp(fi, PImage(fi-> object), 32, cLinesWorth))
980 return false;
981 break;
982 default:
983 outcd("Unsupported bit depth %d, expected 16 or 32", l-> bpp);
984 }
985 break;
986 default:
987 outc("compression type not uncompressed, RLE4 or RLE8");
988 }
989 } else {
990 /* OS/2 */
991 if ( req_seek(fd, l-> offBits, SEEK_SET) < 0)
992 outs(fd);
993 if ( !req_read_big(fi, cLinesWorth, l->h, data))
994 return false;
995 }
996 EVENT_SCANLINES_FINISHED(fi, SCANLINES_DIR_BOTTOM_TO_TOP);
997
998 return true;
999 }
1000
1001 static void
close_load(PImgCodec instance,PImgLoadFileInstance fi)1002 close_load( PImgCodec instance, PImgLoadFileInstance fi)
1003 {
1004 LoadRec * l = ( LoadRec *) fi-> instance;
1005 free( l);
1006 }
1007
1008 static HV *
save_defaults(PImgCodec c)1009 save_defaults( PImgCodec c)
1010 {
1011 HV * profile = newHV();
1012 pset_i( OS2, 0);
1013 pset_i( HotSpotX, 0);
1014 pset_i( HotSpotY, 0);
1015 pset_i( ImportantColors, 0);
1016 pset_i( XResolution, 0);
1017 pset_i( YResolution, 0);
1018 return profile;
1019 }
1020
1021 static void *
open_save(PImgCodec instance,PImgSaveFileInstance fi)1022 open_save( PImgCodec instance, PImgSaveFileInstance fi)
1023 {
1024 return (void*)1;
1025 }
1026
1027 static Bool
save(PImgCodec instance,PImgSaveFileInstance fi)1028 save( PImgCodec instance, PImgSaveFileInstance fi)
1029 {
1030 dPROFILE;
1031 PImage i = ( PImage) fi-> object;
1032 HV * profile = fi-> objectExtras;
1033 int cRGB;
1034 PImgIORequest fd = fi-> req;
1035 RGBColor rgb_1bpp[2], * palette = i-> palette;
1036
1037 Bool os2 = false;
1038 word xHotspot = 0;
1039 word yHotspot = 0;
1040 dword cclrImportant = 0;
1041 dword cxResolution = 0;
1042 dword cyResolution = 0;
1043
1044 if ( pexist( OS2))
1045 os2 = pget_B( OS2);
1046 if ( pexist( HotSpotX))
1047 xHotspot = pget_i( HotSpotX);
1048 if ( pexist( HotSpotY))
1049 yHotspot = pget_i( HotSpotY);
1050 if ( pexist( ImportantColors))
1051 cclrImportant = pget_i( ImportantColors);
1052 if ( pexist( XResolution))
1053 cxResolution = pget_i( XResolution);
1054 if ( pexist( YResolution))
1055 cyResolution = pget_i( YResolution);
1056
1057 cRGB = ( (1 << (i-> type & imBPP)) & 0x1ff ); /* 1->2, 4->16, 8->256, 24->0 */
1058
1059 /* ...handle messy 1bpp case */
1060 if ( cRGB == 2 ) {
1061 /*
1062 The palette entries inside a 1bpp PM bitmap are not honored, or handled
1063 correctly by most programs. Current thinking is that they have no actual
1064 meaning. Under OS/2 PM, bitmap 1's re fg and 0's are bg, and it is the job of
1065 the displayer to pick fg and bg. We will pick fg=black, bg=white in the bitmap
1066 file we save. If we do not write black and white, we find that most programs
1067 will incorrectly honor these entries giving unpredicatable (and often black on
1068 a black background!) results.
1069 */
1070
1071 /* DK adds: since we swap OS/2 mono palette on load, we do the same
1072 on save. The reason is that if the programmed doesn't care about OS/2
1073 problems (which is most probable), we simply restore the status quo.
1074 If he does (surprise!) then he can also swap the palette manually
1075 */
1076 memcpy( &rgb_1bpp, palette, sizeof(RGBColor) * 2);
1077 swap_pal( rgb_1bpp);
1078 palette = rgb_1bpp;
1079 }
1080
1081 if ( os2 ) {
1082 word usType = BFT_BMAP;
1083 dword cbFix = (dword) 12;
1084 word cx = (word) i->w;
1085 word cy = (word) i->h;
1086 word cPlanes = (word) 1;
1087 word cBitCount = (word) i->type & imBPP;
1088 int cLinesWorth = (((cBitCount * cx + 31) / 32) * cPlanes) * 4;
1089 dword offBits = (dword) 26 + cRGB * (dword) 3;
1090 dword cbSize = offBits + (dword) cy * (dword) cLinesWorth;
1091 int j, total, actual;
1092
1093 write_word(fd, usType);
1094 write_dword(fd, cbSize);
1095 write_word(fd, xHotspot);
1096 write_word(fd, yHotspot);
1097 write_dword(fd, offBits);
1098 write_dword(fd, cbFix);
1099 write_word(fd, cx);
1100 write_word(fd, cy);
1101 write_word(fd, cPlanes);
1102 write_word(fd, cBitCount);
1103
1104 for ( j = 0; j < cRGB; j++, palette++ ) {
1105 byte b[3];
1106 b[0] = palette-> b;
1107 b[1] = palette-> g;
1108 b[2] = palette-> r;
1109 if ( req_write(fd, 3, b) != 3 )
1110 outw(fd);
1111 }
1112
1113 total = i->h * cLinesWorth;
1114 actual = req_write(fd, total, i-> data);
1115 if ( actual != total )
1116 outw(fd);
1117 } else {
1118 /* Windows */
1119 word usType = BFT_BMAP;
1120 dword cbFix = (dword) 40;
1121 dword cx = (dword) i->w;
1122 dword cy = (dword) i->h;
1123 word cPlanes = (word) 1;
1124 word cBitCount = (word) i->type & imBPP;
1125 int cLinesWorth = (((cBitCount * (int) cx + 31) / 32) * cPlanes) * 4;
1126 dword offBits = (dword) 54 + cRGB * (dword) 4;
1127 dword cbSize = offBits + (dword) cy * (dword) cLinesWorth;
1128 dword ulCompression = BCA_UNCOMP;
1129 dword cbImage = (dword) cLinesWorth * (dword) i->h;
1130 dword cclrUsed = i-> palSize;
1131 int j, total, actual;
1132
1133 write_word(fd, usType);
1134 write_dword(fd, cbSize);
1135 write_word(fd, xHotspot);
1136 write_word(fd, yHotspot);
1137 write_dword(fd, offBits);
1138
1139 write_dword(fd, cbFix);
1140 write_dword(fd, cx);
1141 write_dword(fd, cy);
1142 write_word(fd, cPlanes);
1143 write_word(fd, cBitCount);
1144 write_dword(fd, ulCompression);
1145 write_dword(fd, cbImage);
1146 write_dword(fd, cxResolution);
1147 write_dword(fd, cyResolution);
1148 write_dword(fd, cclrUsed);
1149 write_dword(fd, cclrImportant);
1150
1151 for ( j = 0; j < cRGB; j++, palette++ ) {
1152 byte b[4];
1153
1154 b[0] = palette-> b;
1155 b[1] = palette-> g;
1156 b[2] = palette-> r;
1157 b[3] = 0;
1158 if ( req_write(fd, 4, b) != 4 )
1159 outw(fd);
1160 }
1161
1162 total = i-> h * cLinesWorth;
1163 actual = req_write(fd, total, i-> data);
1164 if ( actual != total )
1165 outw(fd);
1166 }
1167
1168 return true;
1169 }
1170
1171 static void
close_save(PImgCodec instance,PImgSaveFileInstance fi)1172 close_save( PImgCodec instance, PImgSaveFileInstance fi)
1173 {
1174 }
1175
1176 void
apc_img_codec_bmp(void)1177 apc_img_codec_bmp( void )
1178 {
1179 struct ImgCodecVMT vmt;
1180 memcpy( &vmt, &CNullImgCodecVMT, sizeof( CNullImgCodecVMT));
1181 vmt. init = init;
1182 vmt. open_load = open_load;
1183 vmt. load = load;
1184 vmt. close_load = close_load;
1185 vmt. save_defaults = save_defaults;
1186 vmt. open_save = open_save;
1187 vmt. save = save;
1188 vmt. close_save = close_save;
1189 apc_img_register( &vmt, NULL);
1190 }
1191
1192 #ifdef __cplusplus
1193 }
1194 #endif
1195