source: trunk/JPGPROC/source/gbmsrc/gbmlbm.c@ 2

Last change on this file since 2 was 2, checked in by stevenhl, 8 years ago

Import sources from cwmm-full.zip dated 2005-03-21

File size: 26.4 KB
Line 
1/*
2
3gbmlbm.c - Amiga IFF / ILBM format
4
5BEWARE: This code at present assumes that the red,green and blue planes are
6stored in that order within a true 24 bpp IFF file. I have no testcase or
7documentation to independantly verify this.
8
9*/
10
11/*...sincludes:0:*/
12#include <stdio.h>
13#include <ctype.h>
14#include <stddef.h>
15#include <stdlib.h>
16#include <string.h>
17#include "gbm.h"
18#include "gbmhelp.h"
19
20/*...vgbm\46\h:0:*/
21/*...vgbmhelp\46\h:0:*/
22/*...e*/
23
24/*...smapping:0:*/
25/*...sget_dword:0:*/
26static dword get_dword(byte *b)
27 {
28 return (dword) b[3] +
29 (((dword) b[2]) << 8) +
30 (((dword) b[1]) << 16) +
31 (((dword) b[0]) << 24) ;
32 }
33/*...e*/
34/*...sget_word:0:*/
35static word get_word(byte *b)
36 {
37 return (word) b[1] +
38 (((word) b[0]) << 8) ;
39 }
40/*...e*/
41/*...sput_dword:0:*/
42static void put_dword(byte *b, dword n)
43 {
44 b[3] = (byte) n;
45 n >>= 8;
46 b[2] = (byte) n;
47 n >>= 8;
48 b[1] = (byte) n;
49 n >>= 8;
50 b[0] = (byte) n;
51 }
52/*...e*/
53/*...sput_word:0:*/
54static void put_word(byte *b, word n)
55 {
56 b[1] = (byte) n;
57 n >>= 8;
58 b[0] = (byte) n;
59 }
60/*...e*/
61/*...e*/
62
63static GBMFT lbm_gbmft =
64 {
65 "ILBM",
66 "Amiga IFF / ILBM Interleaved bitmap",
67 "IFF LBM",
68 GBM_FT_R1|GBM_FT_R4|GBM_FT_R8|GBM_FT_R24|
69 GBM_FT_W1|GBM_FT_W4|GBM_FT_W8|GBM_FT_W24,
70 };
71
72#define GBM_ERR_LBM_FORM ((GBM_ERR) 900)
73#define GBM_ERR_LBM_ILBM ((GBM_ERR) 901)
74#define GBM_ERR_LBM_BMHD_2 ((GBM_ERR) 902)
75#define GBM_ERR_LBM_BMHD_0 ((GBM_ERR) 903)
76#define GBM_ERR_LBM_BMHD_SIZE ((GBM_ERR) 904)
77#define GBM_ERR_LBM_BPP ((GBM_ERR) 905)
78#define GBM_ERR_LBM_CMAP_SIZE ((GBM_ERR) 906)
79#define GBM_ERR_LBM_COMP ((GBM_ERR) 907)
80#define GBM_ERR_LBM_CAMG_SIZE ((GBM_ERR) 908)
81#define GBM_ERR_LBM_SHAM_VER ((GBM_ERR) 909)
82
83typedef struct
84 {
85 byte pal[0x100 * 3];
86 dword body, size_body;
87 byte actual_bpp, comp;
88 long sham;
89 } LBM_PRIV;
90
91#define CAMG_LACE 0x00000004
92#define CAMG_EHB 0x00000080
93#define CAMG_HAM 0x00000800
94#define CAMG_1000 0x00001000 /* Meaning unknown */
95#define CAMG_4000 0x00004000 /* Meaning unknown */
96#define CAMG_HIRES 0x00008000
97#define CAMG_20000 0x00020000 /* Meaning unknown */
98
99/*...slbm_qft:0:*/
100GBM_ERR lbm_qft(GBMFT *gbmft)
101 {
102 *gbmft = lbm_gbmft;
103 return GBM_ERR_OK;
104 }
105/*...e*/
106/*...slbm_rhdr:0:*/
107GBM_ERR lbm_rhdr(const char *fn, int fd, GBM *gbm, const char *opt)
108 {
109 LBM_PRIV *priv = (LBM_PRIV *) gbm->priv;
110 byte b[20];
111 int w, h, bpp, actual_size_cmap = 0;
112 BOOLEAN had_bmhd = FALSE, had_cmap = FALSE, had_body = FALSE;
113 dword camg = 0;
114
115 fn=fn; opt=opt; /* Suppress 'unref arg' compiler warnings */
116
117 if ( gbm_file_read(fd, b, 12) != 12 )
118 return GBM_ERR_READ;
119
120 if ( memcmp(b, "FORM", 4) )
121 return GBM_ERR_LBM_FORM;
122
123 if ( memcmp(b + 8, "ILBM", 4) )
124 return GBM_ERR_LBM_ILBM;
125
126 priv->sham = -1L;
127
128 while ( !had_bmhd || !had_cmap || !had_body )
129 {
130 dword size;
131
132 if ( gbm_file_read(fd, b, 8) != 8 )
133 return GBM_ERR_READ;
134 size = get_dword(b + 4);
135 if ( !memcmp(b, "BMHD", 4) )
136/*...sbitmap header:24:*/
137{
138if ( had_bmhd )
139 return GBM_ERR_LBM_BMHD_2;
140
141if ( size != 20 )
142 return GBM_ERR_LBM_BMHD_SIZE;
143
144if ( gbm_file_read(fd, b, 20) != 20 )
145 return GBM_ERR_READ;
146
147priv->comp = b[10];
148if ( priv->comp != 0 && priv->comp != 1 )
149 /* Expect compression type to be uncomp or RLE */
150 return GBM_ERR_LBM_COMP;
151
152w = get_word(b);
153h = get_word(b + 2);
154
155if ( w < 0 || w > 10000 || h < 0 || h > 10000 )
156 return GBM_ERR_BAD_SIZE;
157
158priv->actual_bpp = b[8];
159
160switch ( priv->actual_bpp )
161 {
162 case 1:
163 bpp = 1; break;
164 case 2: case 3: case 4:
165 bpp = 4; break;
166 case 5: case 6: case 7: case 8:
167 bpp = 8; break;
168 case 24:
169 bpp = 24; had_cmap = TRUE; break;
170 default:
171 return GBM_ERR_LBM_BPP;
172 }
173
174if ( priv->actual_bpp == 6 )
175 /* In case no CAMG chunk present */
176 /* Assume HAM6, and will probably be right */
177 camg = CAMG_HAM;
178
179had_bmhd = TRUE;
180}
181/*...e*/
182 else if ( !memcmp(b, "CAMG", 4) )
183/*...sC\61\ Amiga mode info:24:*/
184{
185if ( !had_bmhd )
186 return GBM_ERR_LBM_BMHD_0;
187
188if ( size != 4 )
189 return GBM_ERR_LBM_CAMG_SIZE;
190
191if ( gbm_file_read(fd, b, 4) != 4 )
192 return GBM_ERR_READ;
193
194camg = get_dword(b);
195}
196/*...e*/
197 else if ( !memcmp(b, "CMAP", 4) )
198/*...scolour map:24:*/
199{
200if ( !had_bmhd )
201 return GBM_ERR_LBM_BMHD_0;
202actual_size_cmap = size;
203if ( (dword) gbm_file_read(fd, priv->pal, size) != size )
204 return GBM_ERR_READ;
205had_cmap = TRUE;
206}
207/*...e*/
208 else if ( !memcmp(b, "SHAM", 4) )
209/*...ssham:24:*/
210{
211if ( gbm_file_read(fd, b, 2) != 2 )
212 return GBM_ERR_READ;
213
214if ( get_word(b) != 0 )
215 return GBM_ERR_LBM_SHAM_VER;
216
217priv->sham = gbm_file_lseek(fd, 0L, SEEK_CUR);
218
219gbm_file_lseek(fd, ((size - 2 + 1) & ~1), SEEK_CUR);
220}
221/*...e*/
222 else if ( !memcmp(b, "BODY", 4) )
223/*...sbody of data:24:*/
224{
225if ( !had_bmhd )
226 return GBM_ERR_LBM_BMHD_0;
227
228priv->body = (dword) gbm_file_lseek(fd, 0L, SEEK_CUR);
229priv->size_body = size;
230had_body = TRUE;
231}
232/*...e*/
233 else
234 gbm_file_lseek(fd, ((size + 1) & ~1), SEEK_CUR);
235 }
236
237/*...saccount for ehb\44\ ham6 and ham8:8:*/
238{
239int entrys = ( 1 << priv->actual_bpp );
240int size_cmap = entrys * 3;
241BOOLEAN ehb = FALSE, sham = FALSE, ham6 = FALSE, ham8 = FALSE;
242
243if ( priv->sham != -1L )
244 sham = TRUE; /* Allow Sliced HAM mode */
245if ( (camg & CAMG_EHB) != 0 && actual_size_cmap * 2 == size_cmap )
246 ehb = TRUE; /* Allow Extra-HalfBrite mode */
247else if ( (camg & CAMG_HAM) != 0 && actual_size_cmap == 0x10*3 && size_cmap == 0x40*3 )
248 ham6 = TRUE; /* Allow HAM6 mode */
249else if ( (camg & CAMG_HAM) != 0 && actual_size_cmap == 0x40*3 && size_cmap == 0x100*3 )
250 ham8 = TRUE; /* Allow HAM8 mode */
251else if ( priv->actual_bpp == 24 )
252 ; /* Is a real 24 bpp mode */
253else if ( actual_size_cmap != size_cmap )
254 return GBM_ERR_LBM_CMAP_SIZE;
255if ( ehb )
256/*...sreplicate palette:16:*/
257{
258int i;
259for ( i = 0; i < actual_size_cmap; i++ )
260 priv->pal[actual_size_cmap + i] = (priv->pal[i] >> 1);
261}
262/*...e*/
263else if ( ham6 )
264/*...snobble all but top 4 bits of palette entries:16:*/
265{
266int i;
267for ( i = 0; i < 0x10 * 3; i++ )
268 priv->pal[i] &= 0xf0;
269bpp = 24;
270}
271/*...e*/
272else if ( ham8 || sham )
273 bpp = 24;
274}
275/*...e*/
276
277 gbm->w = w;
278 gbm->h = h;
279 gbm->bpp = bpp;
280
281 return GBM_ERR_OK;
282 }
283/*...e*/
284/*...slbm_rpal:0:*/
285GBM_ERR lbm_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
286 {
287 LBM_PRIV *priv = (LBM_PRIV *) gbm->priv;
288 byte *p = priv->pal;
289 int i, entrys = ( 1 << priv->actual_bpp );
290
291 fd=fd; /* Suppress 'unref arg' compiler warning */
292
293 if ( gbm->bpp == 24 )
294 return GBM_ERR_OK;
295
296 for ( i = 0; i < entrys; i++ )
297 {
298 gbmrgb[i].r = *p++;
299 gbmrgb[i].g = *p++;
300 gbmrgb[i].b = *p++;
301 }
302
303 return GBM_ERR_OK;
304 }
305/*...e*/
306/*...slbm_rdata:0:*/
307/*...sget_line:0:*/
308/*...sread_line \45\ compression type 0 \45\ uncompressed:0:*/
309static BOOLEAN read_line(AHEAD *ahead, byte *dest, int w)
310 {
311 while ( w-- )
312 {
313 int b = gbm_read_ahead(ahead);
314 if ( b == -1 )
315 return FALSE;
316 *dest++ = b;
317 }
318 return TRUE;
319 }
320/*...e*/
321/*...sdecode_line \45\ compression type 1 \45\ RLE:0:*/
322static BOOLEAN decode_line(AHEAD *ahead, byte *dest, int w)
323 {
324 int x = 0;
325
326 while ( x < w )
327 {
328 int c = gbm_read_ahead(ahead);
329
330 if ( c == -1 )
331 return FALSE;
332
333 if ( c & 0x80 )
334 {
335 int cnt = (0x100 - c + 1);
336
337 memset(dest, (byte) gbm_read_ahead(ahead), cnt);
338 x += cnt;
339 dest += cnt;
340 }
341 else
342 {
343 int cnt = (c + 1);
344
345 x += cnt;
346 while ( cnt-- )
347 {
348 int b = gbm_read_ahead(ahead);
349 if ( b == -1 )
350 return FALSE;
351 *dest++ = (byte) b;
352 }
353 }
354 }
355 return TRUE;
356 }
357/*...e*/
358
359static BOOLEAN get_line(LBM_PRIV *priv, AHEAD *ahead, byte *dest, int w)
360 {
361 switch ( priv->comp )
362 {
363 case 0:
364 return read_line(ahead, dest, w);
365 case 1:
366 return decode_line(ahead, dest, w);
367 }
368 return FALSE; /* Shouldn't get here */
369 }
370/*...e*/
371/*...sget_planes_8:0:*/
372static BOOLEAN get_planes_8(
373 AHEAD *ahead,
374 LBM_PRIV *priv,
375 byte *buf,
376 byte *data,
377 int w,
378 int n_planes
379 )
380 {
381 int plane, p;
382 int scan = (((((unsigned)w + 7) >> 3)+1)/2)*2;
383
384 memset(data, 0, w);
385 for ( plane = 0, p = 0x01; plane < n_planes; plane++, p <<= 1 )
386 {
387 int i;
388
389 if ( !get_line(priv, ahead, buf, scan) )
390 return FALSE;
391 for ( i = 0; i < w; i++ )
392 if ( buf[(unsigned)i >> 3] & (0x80U >> ((unsigned)i & 7U)) )
393 data[i] |= p;
394 }
395 return TRUE;
396 }
397/*...e*/
398/*...sget_planes_24:0:*/
399static BOOLEAN get_planes_24(
400 AHEAD *ahead,
401 LBM_PRIV *priv,
402 byte *buf,
403 byte *data,
404 int w
405 )
406 {
407 int plane, p, c, i;
408 int scan = (((((unsigned)w + 7) >> 3)+1)/2)*2;
409
410 memset(data, 0, w*3);
411 for ( c = 2; c >= 0; c-- )
412 for ( plane = 0, p = 0x01; plane < 8; plane++, p <<= 1 )
413 {
414 if ( !get_line(priv, ahead, buf, scan) )
415 return FALSE;
416 for ( i = 0; i < w; i++ )
417 if ( buf[(unsigned)i >> 3] & (0x80U >> ((unsigned)i & 7U)) )
418 data[c+i*3] |= p;
419 }
420 return TRUE;
421 }
422/*...e*/
423
424GBM_ERR lbm_rdata(int fd, GBM *gbm, byte *data)
425 {
426 LBM_PRIV *priv = (LBM_PRIV *) gbm->priv;
427 AHEAD *ahead;
428 int stride = ((gbm->w * gbm->bpp + 31) / 32) * 4;
429 int scan = ((((unsigned)(gbm->w + 7) >> 3)+1)/2)*2;
430 gbm_file_lseek(fd, priv->body, SEEK_SET);
431
432 if ( (ahead = gbm_create_ahead(fd)) == NULL )
433 return GBM_ERR_MEM;
434
435 data += ((gbm->h - 1) * stride);
436
437 switch ( gbm->bpp )
438 {
439/*...s24:16:*/
440case 24:
441 {
442 byte *buf;
443 int y;
444
445 if ( (buf = malloc((size_t) scan)) == NULL )
446 {
447 gbm_destroy_ahead(ahead);
448 return GBM_ERR_MEM;
449 }
450
451 if ( priv->actual_bpp == 24 )
452/*...sreal 24 bpp data case:32:*/
453{
454for ( y = 0; y < gbm->h; y++, data -= stride )
455 if ( !get_planes_24(ahead, priv, buf, data, gbm->w) )
456 {
457 free(buf);
458 gbm_destroy_ahead(ahead);
459 return GBM_ERR_READ;
460 }
461}
462/*...e*/
463 else
464/*...sHAM6\44\ HAM8 or SHAM6:32:*/
465{
466byte *ham, *sham_pals;
467int n_sham_pals, sham_inx = 0;
468
469if ( (ham = malloc((size_t) gbm->w)) == NULL )
470 {
471 free(buf);
472 gbm_destroy_ahead(ahead);
473 return GBM_ERR_MEM;
474 }
475
476if ( priv->sham != -1L )
477/*...sread SHAM palettes:40:*/
478/* SHAM holds 200 lines of 16 words each with a 0rgb palette entry */
479/* If <= 200 lines then one line per palette */
480/* Else two lines per palette */
481
482{
483n_sham_pals = ( gbm->h < 200 ) ? gbm->h : 200;
484if ( (sham_pals = malloc((size_t) (n_sham_pals * 16 * 2))) == NULL )
485 {
486 free(ham);
487 free(buf);
488 gbm_destroy_ahead(ahead);
489 return GBM_ERR_MEM;
490 }
491
492gbm_file_lseek(fd, priv->sham, SEEK_SET);
493if ( gbm_file_read(fd, sham_pals, n_sham_pals * 16 * 2) != n_sham_pals * 16 * 2 )
494 {
495 free(sham_pals);
496 free(ham);
497 free(buf);
498 gbm_destroy_ahead(ahead);
499 return GBM_ERR_READ;
500 }
501gbm_file_lseek(fd, priv->body, SEEK_SET);
502}
503/*...e*/
504
505for ( y = 0; y < gbm->h; y++, data -= stride )
506 {
507 if ( !get_planes_8(ahead, priv, buf, ham, gbm->w, priv->actual_bpp) )
508 {
509 if ( priv->sham != -1L )
510 free(sham_pals);
511 free(buf);
512 free(ham);
513 gbm_destroy_ahead(ahead);
514 return GBM_ERR_READ;
515 }
516
517 if ( priv->sham != -1L )
518/*...sconvert from SHAM6 to 24 bit rgb:48:*/
519{
520byte r = 0, g = 0, b = 0;
521int i;
522for ( i = 0; i < gbm->w; i++ )
523 {
524 byte val = (ham[i] & 0x0f);
525 switch ( ham[i] & 0x30 )
526 {
527 case 0x00:
528 {
529 word pal = get_word(sham_pals + ((sham_inx * 16 + val) * 2));
530 r = (byte) ((pal & 0x0f00U) >> 4);
531 g = (byte) (pal & 0x00f0U) ;
532 b = (byte) ((pal & 0x000fU) << 4);
533 }
534 break;
535 case 0x10:
536 b = (val << 4);
537 break;
538 case 0x20:
539 r = (val << 4);
540 break;
541 case 0x30:
542 g = (val << 4);
543 break;
544 }
545 data[i * 3 ] = b;
546 data[i * 3 + 1] = g;
547 data[i * 3 + 2] = r;
548 }
549if ( gbm->h <= 200 || (y & 1) != 0 )
550 if ( ++sham_inx == n_sham_pals )
551 sham_inx = 0;
552}
553/*...e*/
554 else if ( priv->actual_bpp == 6 )
555/*...sconvert from HAM6 to 24 bit rgb:48:*/
556{
557byte r = 0, g = 0, b = 0;
558int i;
559for ( i = 0; i < gbm->w; i++ )
560 {
561 byte val = (ham[i] & 0x0f);
562 switch ( ham[i] & 0x30 )
563 {
564 case 0x00:
565 r = priv->pal[val * 3 ];
566 g = priv->pal[val * 3 + 1];
567 b = priv->pal[val * 3 + 2];
568 break;
569 case 0x10:
570 b = (val << 4);
571 break;
572 case 0x20:
573 r = (val << 4);
574 break;
575 case 0x30:
576 g = (val << 4);
577 break;
578 }
579 data[i * 3 ] = b;
580 data[i * 3 + 1] = g;
581 data[i * 3 + 2] = r;
582 }
583}
584/*...e*/
585 else
586/*...sconvert from HAM8 to 24 bit rgb:48:*/
587{
588byte r = 0, g = 0, b = 0;
589int i;
590for ( i = 0; i < gbm->w; i++ )
591 {
592 byte val = (ham[i] & 0x3f);
593 switch ( ham[i] & 0xc0 )
594 {
595 case 0x00:
596 r = priv->pal[val * 3 ];
597 g = priv->pal[val * 3 + 1];
598 b = priv->pal[val * 3 + 2];
599 break;
600 case 0x40:
601 b = ((r & 0x03) | (val << 2));
602 break;
603 case 0x80:
604 r = ((b & 0x03) | (val << 2));
605 break;
606 case 0xc0:
607 g = ((g & 0x03) | (val << 2));
608 break;
609 }
610 data[i * 3 ] = b;
611 data[i * 3 + 1] = g;
612 data[i * 3 + 2] = r;
613 }
614}
615/*...e*/
616 }
617
618if ( priv->sham != -1L )
619 free(sham_pals);
620free(ham);
621}
622/*...e*/
623
624 free(buf);
625 }
626 break;
627/*...e*/
628/*...s8:16:*/
629case 8:
630 {
631 byte *buf;
632 int y;
633
634 if ( (buf = malloc((size_t) scan)) == NULL )
635 {
636 gbm_destroy_ahead(ahead);
637 return GBM_ERR_MEM;
638 }
639
640 for ( y = 0; y < gbm->h; y++, data -= stride )
641 if ( !get_planes_8(ahead, priv, buf, data, gbm->w, priv->actual_bpp) )
642 {
643 free(buf);
644 gbm_destroy_ahead(ahead);
645 return GBM_ERR_READ;
646 }
647
648 free(buf);
649 }
650 break;
651/*...e*/
652/*...s4:16:*/
653case 4:
654 {
655 byte *buf;
656 int y;
657
658 if ( (buf = malloc((size_t) scan)) == NULL )
659 {
660 gbm_destroy_ahead(ahead);
661 return GBM_ERR_MEM;
662 }
663
664 for ( y = 0; y < gbm->h; y++, data -= stride )
665 {
666 int plane, p;
667
668 memset(data, 0, stride);
669 for ( plane = 0, p = 0x11; plane < priv->actual_bpp; plane++,p <<= 1 )
670 {
671 int i, mask;
672
673 if ( !get_line(priv, ahead, buf, scan) )
674 {
675 free(buf);
676 gbm_destroy_ahead(ahead);
677 return GBM_ERR_READ;
678 }
679 for ( i = 0, mask = 0xf0; i < gbm->w; i++, mask ^= 0xff )
680 if ( buf[(unsigned)i >> 3] & (0x80U >> ((unsigned)i & 7U)) )
681 data[(unsigned)i >> 1] |= ((unsigned)p & (unsigned)mask);
682 }
683 }
684
685 free(buf);
686 }
687 break;
688/*...e*/
689/*...s1:16:*/
690case 1:
691 {
692 int y;
693
694 for ( y = 0; y < gbm->h; y++, data -= stride )
695 if ( !get_line(priv, ahead, data, scan) )
696 {
697 gbm_destroy_ahead(ahead);
698 return GBM_ERR_READ;
699 }
700 }
701 break;
702/*...e*/
703 }
704
705 gbm_destroy_ahead(ahead);
706
707 return GBM_ERR_OK;
708 }
709/*...e*/
710/*...slbm_w:0:*/
711/*...swrite_bmhd:0:*/
712static GBM_ERR write_bmhd(int fd, const GBM *gbm, int bpp, const char *opt)
713 {
714 byte bmhd[8+20];
715 int xpos = 0, ypos = 0, transcol = 0, xaspect = 1, yaspect = 1;
716 int xscreen = gbm->w, yscreen = gbm->h;
717
718/*...soutput options:8:*/
719{
720const char *s;
721
722if ( (s = gbm_find_word_prefix(opt, "xpos=")) != NULL )
723 sscanf(s + 5, "%d", &xpos);
724
725if ( (s = gbm_find_word_prefix(opt, "ypos=")) != NULL )
726 sscanf(s + 5, "%d", &ypos);
727
728if ( (s = gbm_find_word_prefix(opt, "transcol=")) != NULL )
729 sscanf(s + 9, "%d", &transcol);
730
731if ( (s = gbm_find_word_prefix(opt, "xaspect=")) != NULL )
732 sscanf(s + 8, "%d", &xaspect);
733
734if ( (s = gbm_find_word_prefix(opt, "yaspect=")) != NULL )
735 sscanf(s + 8, "%d", &yaspect);
736
737if ( (s = gbm_find_word_prefix(opt, "xscreen=")) != NULL )
738 sscanf(s + 8, "%d", &xscreen);
739
740if ( (s = gbm_find_word_prefix(opt, "yscreen=")) != NULL )
741 sscanf(s + 8, "%d", &yscreen);
742}
743/*...e*/
744
745 memcpy(bmhd, "BMHD", 4);
746 put_dword(bmhd+4, (dword) 20);
747 put_word(bmhd+8, (word) gbm->w);
748 put_word(bmhd+8+2, (word) gbm->h);
749 put_word(bmhd+8+4, (word) xpos);
750 put_word(bmhd+8+6, (word) ypos);
751 bmhd[8+8] = (byte) bpp;
752
753 bmhd[8+9] = 0; /* Masking 0=None, 1=Mask, 2=Transparent, 3=Lasso */
754 bmhd[8+10] = 1; /* Compression, 0=None, 1=RLE */
755 bmhd[8+11] = 0; /* Unused */
756 put_word(bmhd+8+12, (word) transcol); /* Transparent colour */
757 bmhd[8+14] = (byte) xaspect; /* X Aspect (often 10) */
758 bmhd[8+15] = (byte) yaspect; /* Y Aspect (often 11) */
759
760 put_word(bmhd+8+16, (word) xscreen); /* Screen width */
761 put_word(bmhd+8+18, (word) yscreen); /* Screen height */
762
763 if ( gbm_file_write(fd, bmhd, 8+20) != 8+20 )
764 return GBM_ERR_WRITE;
765
766 return GBM_ERR_OK;
767 }
768/*...e*/
769/*...swrite_camg:0:*/
770static GBM_ERR write_camg(int fd, dword camg_value)
771 {
772 byte camg[8+4];
773
774 memcpy(camg, "CAMG", 4);
775 put_dword(camg+4, (dword) 4);
776 put_dword(camg+8, camg_value);
777
778 if ( gbm_file_write(fd, camg, 8+4) != 8+4 )
779 return GBM_ERR_WRITE;
780
781 return GBM_ERR_OK;
782 }
783/*...e*/
784/*...swrite_cmap:0:*/
785static GBM_ERR write_cmap(int fd, const GBMRGB *gbmrgb, int bpp)
786 {
787 byte cmap[8+0x100*3];
788 int i, entrys = ( 1 << bpp );
789 int size_cmap = 3 * entrys;
790
791 memcpy(cmap, "CMAP", 4);
792 put_dword(cmap+4, (dword) size_cmap);
793 for ( i = 0; i < entrys; i++ )
794 {
795 cmap[8+i*3+0] = gbmrgb[i].r;
796 cmap[8+i*3+1] = gbmrgb[i].g;
797 cmap[8+i*3+2] = gbmrgb[i].b;
798 }
799
800 if ( gbm_file_write(fd, cmap, 8 + size_cmap) != 8 + size_cmap )
801 return GBM_ERR_WRITE;
802
803 return GBM_ERR_OK;
804 }
805/*...e*/
806/*...swrite_body:0:*/
807/*...slbm_rle:0:*/
808/*...slbm_run:0:*/
809static byte lbm_run(const byte *src, int n_src)
810 {
811 byte cnt = 1;
812 byte b = *src++;
813
814 --n_src;
815 while ( cnt < 0x81 && n_src > 0 && *src == b )
816 { cnt++; n_src--; src++; }
817
818 return cnt;
819 }
820/*...e*/
821/*...slbm_lit:0:*/
822static byte lbm_lit(const byte *src, int n_src)
823 {
824 byte cnt = 1;
825
826 ++src; --n_src;
827 while ( cnt < 0x80 && n_src > 0 && *src != src[-1] )
828 { cnt++; n_src--; src++; }
829
830 return cnt;
831 }
832/*...e*/
833
834static void lbm_rle(const byte *src, int n_src, byte *dst, int *n_dst)
835 {
836 *n_dst = 0;
837 while ( n_src )
838 {
839 byte len;
840
841 if ( (len = lbm_run(src, n_src)) > 1 )
842 {
843 *dst++ = (byte) (0x100 - (len - 1));
844 *dst++ = *src;
845 (*n_dst) += 2;
846 }
847 else
848 {
849 len = lbm_lit(src, n_src);
850 *dst++ = (byte) (len - 1);
851 memcpy(dst, src, len);
852 dst += len;
853 (*n_dst) += ( 1 + len );
854 }
855 src += len;
856 n_src -= len;
857 }
858 }
859/*...e*/
860
861static GBM_ERR write_body(int fd, const GBM *gbm, int bpp, int n_planes, const byte *data, long *end)
862 {
863 int scan = ((((gbm->w + 7) / 8)+1)/2)*2;
864 int stride = ((gbm->w * bpp + 31) / 32) * 4;
865 byte *comp;
866 int n_comp;
867 byte body[8];
868 long offset_body, offset_end;
869
870 memcpy(body, "BODY", 4);
871 /* body[4..7] will be filled in later */
872
873 if ( gbm_file_write(fd, body, 8) != 8 )
874 return GBM_ERR_WRITE;
875
876 offset_body = gbm_file_lseek(fd, 0L, SEEK_CUR);
877
878 data += ( gbm->h - 1 ) * stride;
879
880 if ( (comp = malloc((size_t) (scan * 3))) == NULL )
881 return GBM_ERR_MEM;
882
883 switch ( bpp )
884 {
885/*...s24:16:*/
886case 24:
887 {
888 int y, c, p, plane, j;
889 byte *buf;
890
891 if ( (buf = malloc((size_t) scan)) == NULL )
892 {
893 free(comp);
894 return GBM_ERR_MEM;
895 }
896
897 for ( y = 0; y < gbm->h; y++, data -= stride )
898 for ( c = 2; c >= 0; c-- )
899 for ( plane = 0, p = 0x01; plane < 8; plane++, p <<= 1 )
900 {
901 memset(buf, 0, scan);
902 for ( j = 0; j < gbm->w; j++ )
903 if ( data[c+j*3] & p )
904 buf[(unsigned)j >> 3] |= (0x80U >> ((unsigned)j & 7U));
905 lbm_rle(buf, scan, comp, &n_comp);
906 if ( gbm_file_write(fd, comp, n_comp) != n_comp )
907 {
908 free(buf);
909 free(comp);
910 return GBM_ERR_WRITE;
911 }
912 }
913 free(buf);
914 }
915 break;
916/*...e*/
917/*...s8:16:*/
918case 8:
919 {
920 int y, p, plane, j;
921 byte *buf;
922
923 if ( (buf = malloc((size_t) scan)) == NULL )
924 {
925 free(comp);
926 return GBM_ERR_MEM;
927 }
928
929 for ( y = 0; y < gbm->h; y++, data -= stride )
930 for ( plane = 0, p = 0x01; plane < n_planes; plane++, p <<= 1 )
931 {
932 memset(buf, 0, scan);
933 for ( j = 0; j < gbm->w; j++ )
934 if ( data[j] & p )
935 buf[(unsigned)j >> 3] |= (0x80U >> ((unsigned)j & 7U));
936 lbm_rle(buf, scan, comp, &n_comp);
937 if ( gbm_file_write(fd, comp, n_comp) != n_comp )
938 {
939 free(buf);
940 free(comp);
941 return GBM_ERR_WRITE;
942 }
943 }
944 free(buf);
945 }
946 break;
947/*...e*/
948/*...s4:16:*/
949case 4:
950 {
951 int y, p, j, mask;
952 byte *buf;
953
954 if ( (buf = malloc((size_t) scan)) == NULL )
955 {
956 free(comp);
957 return GBM_ERR_MEM;
958 }
959
960 for ( y = 0; y < gbm->h; y++, data -= stride )
961 for ( p = 0x11; p <= 0x88; p <<= 1 )
962 {
963 memset(buf, 0, scan);
964 for ( j = 0, mask = 0xf0; j < gbm->w; j++, mask ^= 0xff )
965 if ( data[(unsigned)j >> 1] & ((unsigned)p & (unsigned)mask) )
966 buf[(unsigned)j >> 3] |= (0x80U >> ((unsigned)j & 7U));
967 lbm_rle(buf, scan, comp, &n_comp);
968 if ( gbm_file_write(fd, comp, n_comp) != n_comp )
969 {
970 free(buf);
971 free(comp);
972 return GBM_ERR_WRITE;
973 }
974 }
975 free(buf);
976 }
977 break;
978/*...e*/
979/*...s1:16:*/
980case 1:
981 {
982 int y;
983
984 for ( y = 0; y < gbm->h; y++, data -= stride )
985 {
986 lbm_rle(data, scan, comp, &n_comp);
987 if ( gbm_file_write(fd, comp, n_comp) != n_comp )
988 {
989 free(comp);
990 return GBM_ERR_WRITE;
991 }
992 }
993 }
994 break;
995/*...e*/
996 }
997
998 free(comp);
999
1000 offset_end = gbm_file_lseek(fd, 0L, SEEK_CUR);
1001
1002 if ( (offset_end - offset_body) & 1 )
1003 /* BODY is an odd # of bytes long! oops, better 'fix' this */
1004 {
1005 byte b = 0; /* Pad byte must be zero */
1006 if ( gbm_file_write(fd, &b, 1) != 1 )
1007 return GBM_ERR_WRITE;
1008 offset_end++;
1009 }
1010
1011 put_dword(body + 4, offset_end - offset_body);
1012 gbm_file_lseek(fd, offset_body - 4L, SEEK_SET);
1013 if ( gbm_file_write(fd, body + 4, 4) != 4 )
1014 return GBM_ERR_WRITE;
1015
1016 *end = offset_end;
1017
1018 return GBM_ERR_OK;
1019 }
1020/*...e*/
1021
1022static byte posdiff[0x100];
1023#define absdiff(a,b) posdiff[(byte)((a)-(b))]
1024/*...slbm_build_abstab:0:*/
1025static void lbm_build_abstab(void)
1026 {
1027 int i;
1028
1029 for ( i = 0; i < 0x80; i++ )
1030 posdiff[i] = (byte) i;
1031 for ( i = 1; i <= 0x80; i++ )
1032 posdiff[0x100 - i] = (byte) i;
1033 }
1034/*...e*/
1035/*...slbm_ham6:0:*/
1036/*
1037We will assume a simple greyscale palette, to make life easy.
1038Palette entries 0 to 15 will be 00, 11, 22, 33, ... ff.
1039Start the line with a close grey.
1040Try to get as many components right first time.
1041Then for each pixel, pick colours to get as many components right each go.
1042*/
1043
1044static void lbm_ham6(const byte *data, byte *ham, int n)
1045 {
1046 if ( n-- > 0 )
1047 {
1048 byte cb = (*data++ >> 4);
1049 byte cg = (*data++ >> 4);
1050 byte cr = (*data++ >> 4);
1051
1052 switch ( (cr==cg?4:0) + (cg==cb?2:0) + (cb==cr?1:0) )
1053 {
1054/*...s0 \45\ none same:24:*/
1055case 0:
1056 *ham++ = cr = cg = cb = ( cr + cg + cb ) / 3;
1057 break;
1058/*...e*/
1059/*...s1 \45\ r\61\b:24:*/
1060case 1:
1061 *ham++ = cg = cr;
1062 break;
1063/*...e*/
1064/*...s2 \45\ g\61\b:24:*/
1065case 2:
1066 *ham++ = cr = cg;
1067 break;
1068/*...e*/
1069/*...s4 \45\ r\61\g:24:*/
1070case 4:
1071 *ham++ = cb = cr;
1072 break;
1073/*...e*/
1074/*...s7 \45\ r\61\g\61\b:24:*/
1075case 7:
1076 *ham++ = cr;
1077 break;
1078/*...e*/
1079 }
1080
1081 while ( n-- > 0 )
1082 {
1083 byte b = (*data++ >> 4);
1084 byte g = (*data++ >> 4);
1085 byte r = (*data++ >> 4);
1086
1087 switch ( ((cr==r)?4:0) + ((cg==g)?2:0) + ((cb==b)?1:0) )
1088 {
1089/*...s0 \45\ none same:32:*/
1090/*
10913 colour components need modifying.
1092Can take upto 3 pixels to get the desired colour.
1093Picking a grey might fix up 2 or more colour components.
1094Pick r,g or b modify to get as close as possible.
1095*/
1096
1097case 0:
1098 if ( ((r==g)?1:0) + ((g==b)?1:0) + ((b==r)?1:0) > 0 )
1099 { *ham++ = cr = cg = cb = ( (r==g) ? r : b ); }
1100 else if ( absdiff(cr,r) >= absdiff(cg,g) && absdiff(cr,r) >= absdiff(cb,b) )
1101 { *ham++ = (0x20 | r); cr = r; }
1102 else if ( absdiff(cg,g) >= absdiff(cr,r) && absdiff(cg,g) >= absdiff(cb,b) )
1103 { *ham++ = (0x30 | g); cg = g; }
1104 else
1105 { *ham++ = (0x10 | b); cb = b; }
1106 break;
1107/*...e*/
1108/*...s1 \45\ b same:32:*/
1109/*
11102 colour components need modifying.
1111Can take upto 2 pixels to get the desired colour.
1112Picking a grey might fix up both colour components.
1113Pick r or g modify to get as close as possible.
1114After this, we expect to have <= 1 colour components in error.
1115*/
1116
1117case 1:
1118 if ( r == g && g == b )
1119 { *ham++ = cr = cg = b; }
1120 else if ( absdiff(cr,r) >= absdiff(cg,g) )
1121 { *ham++ = (0x20 | r); cr = r; }
1122 else
1123 { *ham++ = (0x30 | g); cg = g; }
1124 break;
1125/*...e*/
1126/*...s2 \45\ g same:32:*/
1127/*
11282 colour components need modifying.
1129Can take upto 2 pixels to get the desired colour.
1130Picking a grey might fix up both colour components.
1131Pick r or b modify to get as close as possible.
1132After this, we expect to have <= 1 colour components in error.
1133*/
1134
1135case 2:
1136 if ( r == g && g == b )
1137 { *ham++ = cr = cb = g; }
1138 else if ( absdiff(cr,r) >= absdiff(cb,b) )
1139 { *ham++ = (0x20 | r); cr = r; }
1140 else
1141 { *ham++ = (0x10 | b); cb = b; }
1142 break;
1143/*...e*/
1144/*...s3 \45\ g\44\b same:32:*/
1145case 3:
1146 *ham++ = (0x20 | r); cr = r;
1147 break;
1148/*...e*/
1149/*...s4 \45\ r same:32:*/
1150/*
11512 colour components need modifying.
1152Can take upto 2 pixels to get the desired colour.
1153Picking a grey might fix up both colour components.
1154Pick g or b modify to get as close as possible.
1155After this, we expect to have <= 1 colour components in error.
1156*/
1157
1158case 4:
1159 if ( r == g && g == b )
1160 { *ham++ = cg = cb = r; }
1161 else if ( absdiff(cg,g) >= absdiff(cb,b) )
1162 { *ham++ = (0x30 | g); cg = g; }
1163 else
1164 { *ham++ = (0x10 | b); cb = b; }
1165 break;
1166/*...e*/
1167/*...s5 \45\ r\44\b same:32:*/
1168case 5:
1169 *ham++ = (0x30 | g); cg = g;
1170 break;
1171/*...e*/
1172/*...s6 \45\ r\44\g same:32:*/
1173case 6:
1174 *ham++ = (0x10 | b); cb = b;
1175 break;
1176/*...e*/
1177/*...s7 \45\ r\44\g\44\b same:32:*/
1178case 7:
1179 *ham++ = (0x10 | b);
1180 break;
1181/*...e*/
1182 }
1183 }
1184 }
1185 }
1186/*...e*/
1187
1188GBM_ERR lbm_w(const char *fn, int fd, const GBM *gbm, const GBMRGB *gbmrgb, const byte *data, const char *opt)
1189 {
1190 byte formilbm[8+4];
1191 long end;
1192 GBM_ERR rc;
1193 BOOLEAN ham6 = ( gbm_find_word(opt, "ham6") != NULL );
1194
1195 fn=fn; /* Suppress 'unref args' compiler warning */
1196
1197 memcpy(formilbm, "FORM", 4);
1198 /* formilbm[4..7] will be filled in later */
1199 memcpy(formilbm+8, "ILBM", 4);
1200 if ( gbm_file_write(fd, formilbm, 8+4) != 8+4 )
1201 return GBM_ERR_WRITE;
1202
1203 if ( gbm->bpp == 24 )
1204 {
1205 if ( ham6 )
1206/*...smap to ham6 write bmhd\44\ camg\44\ cmap and body:24:*/
1207{
1208byte *ham;
1209GBMRGB gbmrgb_grey[0x10];
1210int stride8 = ((gbm->w + 3) & ~3);
1211int stride24 = ((gbm->w * 3 + 3) & ~3);
1212int i;
1213
1214if ( (rc = write_bmhd(fd, gbm, 6, opt)) != GBM_ERR_OK )
1215 return rc;
1216
1217if ( (rc = write_camg(fd, CAMG_HAM)) != GBM_ERR_OK )
1218 return rc;
1219
1220for ( i = 0; i <= 0x10; i++ )
1221 gbmrgb_grey[i].r = gbmrgb_grey[i].g = gbmrgb_grey[i].b = (byte) (i * 0x11);
1222
1223if ( (rc = write_cmap(fd, gbmrgb_grey, 4)) != GBM_ERR_OK )
1224 return rc;
1225
1226if ( (ham = malloc((size_t) (stride8 * gbm->h))) == NULL )
1227 return GBM_ERR_MEM;
1228
1229lbm_build_abstab();
1230for ( i = 0; i < gbm->h; i++ )
1231 lbm_ham6(data + i * stride24, ham + i * stride8, gbm->w);
1232
1233if ( (rc = write_body(fd, gbm, 8, 6, ham, &end)) != GBM_ERR_OK )
1234 {
1235 free(ham);
1236 return rc;
1237 }
1238
1239free(ham);
1240}
1241/*...e*/
1242 else
1243/*...swrite bmhd and body:24:*/
1244{
1245if ( (rc = write_bmhd(fd, gbm, gbm->bpp, opt)) != GBM_ERR_OK )
1246 return rc;
1247if ( (rc = write_body(fd, gbm, gbm->bpp, gbm->bpp, data, &end)) != GBM_ERR_OK )
1248 return rc;
1249}
1250/*...e*/
1251 }
1252 else
1253/*...swrite bmhd\44\ cmap and body:16:*/
1254{
1255if ( (rc = write_bmhd(fd, gbm, gbm->bpp, opt)) != GBM_ERR_OK )
1256 return rc;
1257
1258if ( (rc = write_cmap(fd, gbmrgb, gbm->bpp)) != GBM_ERR_OK )
1259 return rc;
1260
1261if ( (rc = write_body(fd, gbm, gbm->bpp, gbm->bpp, data, &end)) != GBM_ERR_OK )
1262 return rc;
1263}
1264/*...e*/
1265
1266 put_dword(formilbm + 4, end - 8L);
1267 gbm_file_lseek(fd, 4L, SEEK_SET);
1268 if ( gbm_file_write(fd, formilbm + 4, 4) != 4 )
1269 return GBM_ERR_WRITE;
1270
1271 return GBM_ERR_OK;
1272 }
1273/*...e*/
1274/*...slbm_err:0:*/
1275const char *lbm_err(GBM_ERR rc)
1276 {
1277 switch ( (int) rc )
1278 {
1279 case GBM_ERR_LBM_FORM:
1280 return "no FORM signiture";
1281 case GBM_ERR_LBM_ILBM:
1282 return "no ILBM signiture";
1283 case GBM_ERR_LBM_BMHD_2:
1284 return "multiple BMHD bitmap headers";
1285 case GBM_ERR_LBM_BMHD_0:
1286 return "no BMHD bitmap header";
1287 case GBM_ERR_LBM_BMHD_SIZE:
1288 return "bad size of BMHD bitmap header";
1289 case GBM_ERR_LBM_BPP:
1290 return "unsupported/bad bits per pixel";
1291 case GBM_ERR_LBM_CMAP_SIZE:
1292 return "CMAP colour map is the wrong size";
1293 case GBM_ERR_LBM_COMP:
1294 return "compression type not uncompressed nor RLE";
1295 case GBM_ERR_LBM_CAMG_SIZE:
1296 return "CAMG chunk is the wrong size";
1297 case GBM_ERR_LBM_SHAM_VER:
1298 return "Unsupported version of SHAM (not 0)";
1299 }
1300 return NULL;
1301 }
1302/*...e*/
Note: See TracBrowser for help on using the repository browser.