home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
rexx
/
library2
/
gbmrexx
/
gbm
/
gbmlbm.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-09-27
|
27KB
|
1,296 lines
/*
GBMLBM.C Amiga IFF / ILBM format
*/
/*...sincludes:0:*/
#include <stdio.h>
#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <malloc.h>
#ifdef AIX
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "standard.h"
#include "gbm.h"
/*...vgbm\46\h:0:*/
/*...e*/
/*...ssame:0:*/
static BOOLEAN same(char *s1, char *s2, int n)
{
for ( ; n--; s1++, s2++ )
if ( tolower(*s1) != tolower(*s2) )
return ( FALSE );
return ( TRUE );
}
/*...e*/
/*...sfind_word_prefix:0:*/
static char *find_word_prefix(char *str, char *substr)
{
char buf [100+1], *s;
int len = strlen(substr);
for ( s = strtok(strcpy(buf, str), " \t,");
s != NULL;
s = strtok(NULL, " \t,") )
if ( same(s, substr, len) )
return ( str + (s - buf) );
return ( NULL );
}
/*...e*/
/*...smapping:0:*/
/*...sget_dword:0:*/
static dword get_dword(byte *b)
{
return ( (dword) b [3] +
(((dword) b [2]) << 8) +
(((dword) b [1]) << 16) +
(((dword) b [0]) << 24) );
}
/*...e*/
/*...sget_word:0:*/
static word get_word(byte *b)
{
return ( (word) b [1] +
(((word) b [0]) << 8) );
}
/*...e*/
/*...sput_dword:0:*/
static void put_dword(byte *b, dword n)
{
b [3] = (byte) n;
n >>= 8;
b [2] = (byte) n;
n >>= 8;
b [1] = (byte) n;
n >>= 8;
b [0] = (byte) n;
}
/*...e*/
/*...sput_word:0:*/
static void put_word(byte *b, word n)
{
b [1] = (byte) n;
n >>= 8;
b [0] = (byte) n;
}
/*...e*/
/*...e*/
/*...sreading ahead:0:*/
#define AHEAD_BUF 0x4000
typedef struct
{
byte buf [AHEAD_BUF];
int inx, cnt;
int fd;
} AHEAD;
static AHEAD *create_ahead(int fd)
{
AHEAD *ahead;
if ( (ahead = malloc(sizeof(AHEAD))) == NULL )
return ( NULL );
ahead -> inx = 0;
ahead -> cnt = 0;
ahead -> fd = fd;
return ( ahead );
}
static void destroy_ahead(AHEAD *ahead)
{
free(ahead);
}
static int next(AHEAD *ahead)
{
if ( ahead -> inx >= ahead -> cnt )
{
ahead -> cnt = read(ahead -> fd, (char *) ahead -> buf, AHEAD_BUF);
if ( ahead -> cnt <= 0 )
return ( -1 );
ahead -> inx = 0;
}
return ( ahead -> buf [ahead -> inx++] );
}
/*...e*/
static GBMFT lbm_gbmft =
{
"ILBM",
"Amiga IFF / ILBM Interleaved bitmap",
"IFF LBM",
GBM_FT_R1|GBM_FT_R4|GBM_FT_R8|GBM_FT_R24|
GBM_FT_W1|GBM_FT_W4|GBM_FT_W8|GBM_FT_W24,
};
#define GBM_ERR_LBM_FORM ((GBM_ERR) 900)
#define GBM_ERR_LBM_ILBM ((GBM_ERR) 901)
#define GBM_ERR_LBM_BMHD_2 ((GBM_ERR) 902)
#define GBM_ERR_LBM_BMHD_0 ((GBM_ERR) 903)
#define GBM_ERR_LBM_BMHD_SIZE ((GBM_ERR) 904)
#define GBM_ERR_LBM_BPP ((GBM_ERR) 905)
#define GBM_ERR_LBM_CMAP_SIZE ((GBM_ERR) 906)
#define GBM_ERR_LBM_COMP ((GBM_ERR) 907)
#define GBM_ERR_LBM_CAMG_SIZE ((GBM_ERR) 908)
#define GBM_ERR_LBM_SHAM_VER ((GBM_ERR) 909)
typedef struct
{
byte pal [0x100 * 3];
dword body, size_body;
byte actual_bpp, comp;
long sham;
} LBM_PRIV;
#define CAMG_LACE 0x00000004
#define CAMG_EHB 0x00000080
#define CAMG_HAM 0x00000800
#define CAMG_1000 0x00001000 /* Meaning unknown */
#define CAMG_4000 0x00004000 /* Meaning unknown */
#define CAMG_HIRES 0x00008000
#define CAMG_20000 0x00020000 /* Meaning unknown */
/*...slbm_qft:0:*/
GBM_ERR lbm_qft(GBMFT *gbmft)
{
*gbmft = lbm_gbmft;
return ( GBM_ERR_OK );
}
/*...e*/
/*...slbm_rhdr:0:*/
GBM_ERR lbm_rhdr(char *fn, int fd, GBM *gbm, char *opt)
{
LBM_PRIV *priv = (LBM_PRIV *) gbm -> priv;
byte b [20];
int w, h, bpp, actual_size_cmap;
BOOLEAN had_bmhd = FALSE, had_cmap = FALSE, had_body = FALSE;
dword camg = 0;
fn=fn; opt=opt; /* Suppress 'unref arg' compiler warnings */
lseek(fd, 0L, SEEK_SET);
if ( read(fd, b, 12) != 12 )
return ( GBM_ERR_READ );
if ( memcmp(b, "FORM", 4) )
return ( GBM_ERR_LBM_FORM );
if ( memcmp(b + 8, "ILBM", 4) )
return ( GBM_ERR_LBM_ILBM );
priv -> sham = -1L;
while ( !had_bmhd || !had_cmap || !had_body )
{
dword size;
if ( read(fd, b, 8) != 8 )
return ( GBM_ERR_READ );
size = get_dword(b + 4);
if ( !memcmp(b, "BMHD", 4) )
/*...sbitmap header:24:*/
{
if ( had_bmhd )
return ( GBM_ERR_LBM_BMHD_2 );
if ( size != 20 )
return ( GBM_ERR_LBM_BMHD_SIZE );
if ( read(fd, b, 20) != 20 )
return ( GBM_ERR_READ );
priv -> comp = b [10];
if ( priv -> comp != 0 && priv -> comp != 1 )
/* Expect compression type to be uncomp or RLE */
return ( GBM_ERR_LBM_COMP );
w = get_word(b);
h = get_word(b + 2);
if ( w < 0 || w > 10000 || h < 0 || h > 10000 )
return ( GBM_ERR_BAD_SIZE );
priv -> actual_bpp = b [8];
switch ( priv -> actual_bpp )
{
case 1:
bpp = 1; break;
case 2: case 3: case 4:
bpp = 4; break;
case 5: case 6: case 7: case 8:
bpp = 8; break;
default:
return ( GBM_ERR_LBM_BPP );
}
if ( priv -> actual_bpp == 6 )
/* In case no CAMG chunk present */
/* Assume HAM6, and will probably be right */
camg = CAMG_HAM;
had_bmhd = TRUE;
}
/*...e*/
else if ( !memcmp(b, "CAMG", 4) )
/*...sC\61\ Amiga mode info:24:*/
{
if ( !had_bmhd )
return ( GBM_ERR_LBM_BMHD_0 );
if ( size != 4 )
return ( GBM_ERR_LBM_CAMG_SIZE );
if ( read(fd, b, 4) != 4 )
return ( GBM_ERR_READ );
camg = get_dword(b);
}
/*...e*/
else if ( !memcmp(b, "CMAP", 4) )
/*...scolour map:24:*/
{
if ( !had_bmhd )
return ( GBM_ERR_LBM_BMHD_0 );
actual_size_cmap = size;
if ( read(fd, priv -> pal, size) != size )
return ( GBM_ERR_READ );
had_cmap = TRUE;
}
/*...e*/
else if ( !memcmp(b, "SHAM", 4) )
/*...ssham:24:*/
{
if ( read(fd, b, 2) != 2 )
return ( GBM_ERR_READ );
if ( get_word(b) != 0 )
return ( GBM_ERR_LBM_SHAM_VER );
priv -> sham = lseek(fd, 0L, SEEK_CUR);
lseek(fd, ((size - 2 + 1) & ~1), SEEK_CUR);
}
/*...e*/
else if ( !memcmp(b, "BODY", 4) )
/*...sbody of data:24:*/
{
if ( !had_bmhd )
return ( GBM_ERR_LBM_BMHD_0 );
priv -> body = (dword) lseek(fd, 0L, SEEK_CUR);
priv -> size_body = size;
had_body = TRUE;
}
/*...e*/
else
lseek(fd, ((size + 1) & ~1), SEEK_CUR);
}
/*...saccount for ehb\44\ ham6 and ham8:8:*/
{
int entrys = ( 1 << priv -> actual_bpp );
int size_cmap = entrys * 3;
BOOLEAN ehb = FALSE, sham = FALSE, ham6 = FALSE, ham8 = FALSE;
if ( priv -> sham != -1L )
sham = TRUE; /* Allow Sliced HAM mode */
if ( (camg & CAMG_EHB) != 0 && actual_size_cmap * 2 == size_cmap )
ehb = TRUE; /* Allow Extra-HalfBrite mode */
else if ( (camg & CAMG_HAM) != 0 && actual_size_cmap == 0x10*3 && size_cmap == 0x40*3 )
ham6 = TRUE; /* Allow HAM6 mode */
else if ( (camg & CAMG_HAM) != 0 && actual_size_cmap == 0x40*3 && size_cmap == 0x100*3 )
ham8 = TRUE; /* Allow HAM8 mode */
else if ( actual_size_cmap != size_cmap )
return ( GBM_ERR_LBM_CMAP_SIZE );
if ( ehb )
/*...sreplicate palette:16:*/
{
int i;
for ( i = 0; i < actual_size_cmap; i++ )
priv -> pal [actual_size_cmap + i] = (priv -> pal [i] >> 1);
}
/*...e*/
else if ( ham6 )
/*...snobble all but top 4 bits of palette entries:16:*/
{
int i;
for ( i = 0; i < 0x10 * 3; i++ )
priv -> pal [i] &= 0xf0;
bpp = 24;
}
/*...e*/
else if ( ham8 || sham )
bpp = 24;
}
/*...e*/
gbm -> w = w;
gbm -> h = h;
gbm -> bpp = bpp;
return ( GBM_ERR_OK );
}
/*...e*/
/*...slbm_rpal:0:*/
GBM_ERR lbm_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
{
LBM_PRIV *priv = (LBM_PRIV *) gbm -> priv;
byte *p = priv -> pal;
int i, entrys = ( 1 << priv -> actual_bpp );
fd=fd; /* Suppress 'unref arg' compiler warning */
if ( gbm -> bpp == 24 )
return ( GBM_ERR_OK );
for ( i = 0; i < entrys; i++ )
{
gbmrgb [i].r = *p++;
gbmrgb [i].g = *p++;
gbmrgb [i].b = *p++;
}
return ( GBM_ERR_OK );
}
/*...e*/
/*...slbm_rdata:0:*/
/*...sget_line:0:*/
/*...sread_line \45\ compression type 0 \45\ uncompressed:0:*/
static BOOLEAN read_line(AHEAD *ahead, byte *dest, int w)
{
while ( w-- )
{
int b = next(ahead);
if ( b == -1 )
return ( FALSE );
*dest++ = b;
}
return ( TRUE );
}
/*...e*/
/*...sdecode_line \45\ compression type 1 \45\ RLE:0:*/
static BOOLEAN decode_line(AHEAD *ahead, byte *dest, int w)
{
int x = 0;
while ( x < w )
{
int c = next(ahead);
if ( c == -1 )
return ( FALSE );
if ( c & 0x80 )
{
int cnt = (0x100 - c + 1);
memset(dest, next(ahead), cnt);
x += cnt;
dest += cnt;
}
else
{
int cnt = (c + 1);
x += cnt;
while ( cnt-- )
{
int b = next(ahead);
if ( b == -1 )
return ( FALSE );
*dest++ = b;
}
}
}
return ( TRUE );
}
/*...e*/
static BOOLEAN get_line(LBM_PRIV *priv, AHEAD *ahead, byte *dest, int w)
{
switch ( priv -> comp )
{
case 0:
return ( read_line(ahead, dest, w) );
case 1:
return ( decode_line(ahead, dest, w) );
}
return ( FALSE ); /* Shouldn't get here */
}
/*...e*/
/*...sget_planes_8:0:*/
static BOOLEAN get_planes_8(
AHEAD *ahead,
LBM_PRIV *priv,
byte *buf,
byte *data,
int w,
int n_planes
)
{
int plane, p;
int scan = ((w + 7) >> 3);
memset(data, 0, w);
for ( plane = 0, p = 0x01; plane < n_planes; plane++, p <<= 1 )
{
int i;
if ( !get_line(priv, ahead, buf, scan) )
return ( FALSE );
for ( i = 0; i < w; i++ )
if ( buf [i >> 3] & (0x80 >> (i & 7)) )
data [i] |= p;
}
return ( TRUE );
}
/*...e*/
GBM_ERR lbm_rdata(int fd, GBM *gbm, byte *data)
{
LBM_PRIV *priv = (LBM_PRIV *) gbm -> priv;
AHEAD *ahead;
int stride = ((gbm -> w * gbm -> bpp + 31) / 32) * 4;
int scan = ((gbm -> w + 7) >> 3);
lseek(fd, priv -> body, SEEK_SET);
if ( (ahead = create_ahead(fd)) == NULL )
return ( GBM_ERR_MEM );
data += ((gbm -> h - 1) * stride);
switch ( gbm -> bpp )
{
/*...s24:16:*/
/*
Hold and modify.
Or even sliced hold and modify.
*/
case 24:
{
byte *buf, *ham, *sham_pals;
int y, n_sham_pals, sham_inx = 0;
if ( (buf = malloc(scan)) == NULL )
{
destroy_ahead(ahead);
return ( GBM_ERR_MEM );
}
if ( (ham = malloc(gbm -> w)) == NULL )
{
free(buf);
destroy_ahead(ahead);
return ( GBM_ERR_MEM );
}
if ( priv -> sham != -1L )
/*...sread SHAM palettes:32:*/
/* SHAM holds 200 lines of 16 words each with a 0rgb palette entry */
/* If <= 200 lines then one line per palette */
/* Else two lines per palette */
{
n_sham_pals = ( gbm -> h < 200 ) ? gbm -> h : 200;
if ( (sham_pals = malloc(n_sham_pals * 16 * 2)) == NULL )
{
free(ham);
free(buf);
destroy_ahead(ahead);
return ( GBM_ERR_MEM );
}
lseek(fd, priv -> sham, SEEK_SET);
if ( read(fd, sham_pals, n_sham_pals * 16 * 2) != n_sham_pals * 16 * 2 )
{
free(sham_pals);
free(ham);
free(buf);
destroy_ahead(ahead);
return ( GBM_ERR_READ );
}
lseek(fd, priv -> body, SEEK_SET);
}
/*...e*/
for ( y = 0; y < gbm -> h; y++, data -= stride )
{
if ( !get_planes_8(ahead, priv, buf, ham, gbm -> w, priv -> actual_bpp) )
{
if ( priv -> sham != -1L )
free(sham_pals);
free(buf);
free(ham);
destroy_ahead(ahead);
return ( GBM_ERR_READ );
}
if ( priv -> sham != -1L )
/*...sconvert from SHAM6 to 24 bit rgb:40:*/
{
byte r = 0, g = 0, b = 0;
int i;
for ( i = 0; i < gbm -> w; i++ )
{
byte val = (ham [i] & 0x0f);
switch ( ham [i] & 0x30 )
{
case 0x00:
{
word pal = get_word(sham_pals + ((sham_inx * 16 + val) * 2));
r = (byte) ((pal & 0x0f00) >> 4);
g = (byte) (pal & 0x00f0) ;
b = (byte) ((pal & 0x000f) << 4);
}
break;
case 0x10:
b = (val << 4);
break;
case 0x20:
r = (val << 4);
break;
case 0x30:
g = (val << 4);
break;
}
data [i * 3 ] = b;
data [i * 3 + 1] = g;
data [i * 3 + 2] = r;
}
if ( gbm -> h <= 200 || (y & 1) != 0 )
if ( ++sham_inx == n_sham_pals )
sham_inx = 0;
}
/*...e*/
else if ( priv -> actual_bpp == 6 )
/*...sconvert from HAM6 to 24 bit rgb:40:*/
{
byte r = 0, g = 0, b = 0;
int i;
for ( i = 0; i < gbm -> w; i++ )
{
byte val = (ham [i] & 0x0f);
switch ( ham [i] & 0x30 )
{
case 0x00:
r = priv -> pal [val * 3 ];
g = priv -> pal [val * 3 + 1];
b = priv -> pal [val * 3 + 2];
break;
case 0x10:
b = (val << 4);
break;
case 0x20:
r = (val << 4);
break;
case 0x30:
g = (val << 4);
break;
}
data [i * 3 ] = b;
data [i * 3 + 1] = g;
data [i * 3 + 2] = r;
}
}
/*...e*/
else
/*...sconvert from HAM8 to 24 bit rgb:40:*/
{
byte r = 0, g = 0, b = 0;
int i;
for ( i = 0; i < gbm -> w; i++ )
{
byte val = (ham [i] & 0x3f);
switch ( ham [i] & 0xc0 )
{
case 0x00:
r = priv -> pal [val * 3 ];
g = priv -> pal [val * 3 + 1];
b = priv -> pal [val * 3 + 2];
break;
case 0x40:
b = ((r & 0x03) | (val << 2));
break;
case 0x80:
r = ((b & 0x03) | (val << 2));
break;
case 0xc0:
g = ((g & 0x03) | (val << 2));
break;
}
data [i * 3 ] = b;
data [i * 3 + 1] = g;
data [i * 3 + 2] = r;
}
}
/*...e*/
}
if ( priv -> sham != -1L )
free(sham_pals);
free(ham);
free(buf);
}
break;
/*...e*/
/*...s8:16:*/
case 8:
{
byte *buf;
int y;
if ( (buf = malloc(scan)) == NULL )
{
destroy_ahead(ahead);
return ( GBM_ERR_MEM );
}
for ( y = 0; y < gbm -> h; y++, data -= stride )
if ( !get_planes_8(ahead, priv, buf, data, gbm -> w, priv -> actual_bpp) )
{
free(buf);
destroy_ahead(ahead);
return ( GBM_ERR_READ );
}
free(buf);
}
break;
/*...e*/
/*...s4:16:*/
case 4:
{
byte *buf;
int y;
if ( (buf = malloc(scan)) == NULL )
{
destroy_ahead(ahead);
return ( GBM_ERR_MEM );
}
for ( y = 0; y < gbm -> h; y++, data -= stride )
{
int plane, p;
memset(data, 0, stride);
for ( plane = 0, p = 0x11; plane < priv -> actual_bpp; plane++,p <<= 1 )
{
int i, mask;
if ( !get_line(priv, ahead, buf, scan) )
{
free(buf);
destroy_ahead(ahead);
return ( GBM_ERR_READ );
}
for ( i = 0, mask = 0xf0; i < gbm -> w; i++, mask ^= 0xff )
if ( buf [i >> 3] & (0x80 >> (i & 7)) )
data [i >> 1] |= (p & mask);
}
}
free(buf);
}
break;
/*...e*/
/*...s1:16:*/
case 1:
{
int y;
for ( y = 0; y < gbm -> h; y++, data -= stride )
if ( !get_line(priv, ahead, data, scan) )
{
destroy_ahead(ahead);
return ( GBM_ERR_READ );
}
}
break;
/*...e*/
}
destroy_ahead(ahead);
return ( GBM_ERR_OK );
}
/*...e*/
/*...slbm_w:0:*/
/*...swrite_bmhd:0:*/
static GBM_ERR write_bmhd(int fd, GBM *gbm, int bpp, char *opt)
{
byte bmhd [8+20];
int xpos = 0, ypos = 0, transcol = 0, xaspect = 1, yaspect = 1;
int xscreen = gbm -> w, yscreen = gbm -> h;
/*...soutput options:8:*/
{
char *s;
if ( (s = find_word_prefix(opt, "xpos=")) != NULL )
sscanf(s + 5, "%d", &xpos);
if ( (s = find_word_prefix(opt, "ypos=")) != NULL )
sscanf(s + 5, "%d", &ypos);
if ( (s = find_word_prefix(opt, "transcol=")) != NULL )
sscanf(s + 9, "%d", &transcol);
if ( (s = find_word_prefix(opt, "xaspect=")) != NULL )
sscanf(s + 8, "%d", &xaspect);
if ( (s = find_word_prefix(opt, "yaspect=")) != NULL )
sscanf(s + 8, "%d", &yaspect);
if ( (s = find_word_prefix(opt, "xscreen=")) != NULL )
sscanf(s + 8, "%d", &xscreen);
if ( (s = find_word_prefix(opt, "yscreen=")) != NULL )
sscanf(s + 8, "%d", &yscreen);
}
/*...e*/
memcpy(bmhd, "BMHD", 4);
put_dword(bmhd+4, (dword) 20);
put_word(bmhd+8, gbm -> w);
put_word(bmhd+8+2, gbm -> h);
put_word(bmhd+8+4, xpos);
put_word(bmhd+8+6, ypos);
bmhd [8+8] = (byte) bpp;
bmhd [8+9] = 0; /* Masking 0=None, 1=Mask, 2=Transparent, 3=Lasso */
bmhd [8+10] = 1; /* Compression, 0=None, 1=RLE */
bmhd [8+11] = 0; /* Unused */
put_word(bmhd+8+12, (word) transcol); /* Transparent colour */
bmhd [8+14] = (byte) xaspect; /* X Aspect (often 10) */
bmhd [8+15] = (byte) yaspect; /* Y Aspect (often 11) */
put_word(bmhd+8+16, xscreen); /* Screen width */
put_word(bmhd+8+18, yscreen); /* Screen height */
if ( write(fd, bmhd, 8+20) != 8+20 )
return ( GBM_ERR_WRITE );
return ( GBM_ERR_OK );
}
/*...e*/
/*...swrite_camg:0:*/
static GBM_ERR write_camg(int fd, dword camg_value)
{
byte camg [8+4];
memcpy(camg, "CAMG", 4);
put_dword(camg+4, (dword) 4);
put_dword(camg+8, camg_value);
if ( write(fd, camg, 8+4) != 8+4 )
return ( GBM_ERR_WRITE );
return ( GBM_ERR_OK );
}
/*...e*/
/*...swrite_cmap:0:*/
static GBM_ERR write_cmap(int fd, GBMRGB *gbmrgb, int bpp)
{
byte cmap [8+0x100*3];
int i, entrys = ( 1 << bpp );
int size_cmap = 3 * entrys;
memcpy(cmap, "CMAP", 4);
put_dword(cmap+4, (dword) size_cmap);
for ( i = 0; i < entrys; i++ )
{
cmap [8+i*3+0] = gbmrgb [i].r;
cmap [8+i*3+1] = gbmrgb [i].g;
cmap [8+i*3+2] = gbmrgb [i].b;
}
if ( write(fd, cmap, 8 + size_cmap) != 8 + size_cmap )
return ( GBM_ERR_WRITE );
return ( GBM_ERR_OK );
}
/*...e*/
/*...swrite_body:0:*/
/*...slbm_rle:0:*/
/*...slbm_run:0:*/
static byte lbm_run(byte *src, int n_src)
{
byte cnt = 1;
byte b = *src++;
--n_src;
while ( cnt < 0x81 && n_src > 0 && *src == b )
{ cnt++; n_src--; src++; }
return ( cnt );
}
/*...e*/
/*...slbm_lit:0:*/
static byte lbm_lit(byte *src, int n_src)
{
byte cnt = 1;
++src; --n_src;
while ( cnt < 0x81 && n_src > 0 && *src != src [-1] )
{ cnt++; n_src--; src++; }
return ( cnt );
}
/*...e*/
static void lbm_rle(byte *src, int n_src, byte *dst, int *n_dst)
{
*n_dst = 0;
while ( n_src )
{
byte len;
if ( (len = lbm_run(src, n_src)) > 1 )
{
*dst++ = (byte) (0x100 - (len - 1));
*dst++ = *src;
(*n_dst) += 2;
}
else
{
len = lbm_lit(src, n_src);
*dst++ = (byte) (len - 1);
memcpy(dst, src, len);
dst += len;
(*n_dst) += ( 1 + len );
}
src += len;
n_src -= len;
}
}
/*...e*/
static GBM_ERR write_body(int fd, GBM *gbm, int bpp, int n_planes, byte *data, long *end)
{
int scan = (gbm -> w + 7) / 8;
int stride = ((gbm -> w * bpp + 31) / 32) * 4;
byte *comp;
int n_comp;
byte body [8];
long offset_body, offset_end;
memcpy(body, "BODY", 4);
/* body [4..7] will be filled in later */
if ( write(fd, body, 8) != 8 )
return ( GBM_ERR_WRITE );
offset_body = lseek(fd, 0L, SEEK_CUR);
data += ( gbm -> h - 1 ) * stride;
if ( (comp = malloc(scan * 3)) == NULL )
return ( GBM_ERR_MEM );
switch ( bpp )
{
/*...s8:16:*/
case 8:
{
int y;
byte *buf;
if ( (buf = malloc(scan)) == NULL )
{
free(comp);
return ( GBM_ERR_MEM );
}
for ( y = 0; y < gbm -> h; y++, data -= stride )
{
int p, plane;
for ( plane = 0, p = 0x01; plane < n_planes; plane++, p <<= 1 )
{
int j;
memset(buf, 0, scan);
for ( j = 0; j < gbm -> w; j++ )
if ( data [j] & p )
buf [j >> 3] |= (0x80 >> (j & 7));
lbm_rle(buf, scan, comp, &n_comp);
if ( write(fd, comp, n_comp) != n_comp )
{
free(buf);
free(comp);
return ( GBM_ERR_WRITE );
}
}
}
free(buf);
}
break;
/*...e*/
/*...s4:16:*/
case 4:
{
int y;
byte *buf;
if ( (buf = malloc(scan)) == NULL )
{
free(comp);
return ( GBM_ERR_MEM );
}
for ( y = 0; y < gbm -> h; y++, data -= stride )
{
int p;
for ( p = 0x11; p <= 0x88; p <<= 1 )
{
int j, mask;
memset(buf, 0, scan);
for ( j = 0, mask = 0xf0; j < gbm -> w; j++, mask ^= 0xff )
if ( data [j >> 1] & (p & mask) )
buf [j >> 3] |= (0x80 >> (j & 7));
lbm_rle(buf, scan, comp, &n_comp);
if ( write(fd, comp, n_comp) != n_comp )
{
free(buf);
free(comp);
return ( GBM_ERR_WRITE );
}
}
}
free(buf);
}
break;
/*...e*/
/*...s1:16:*/
case 1:
{
int y;
for ( y = 0; y < gbm -> h; y++, data -= stride )
{
lbm_rle(data, scan, comp, &n_comp);
if ( write(fd, comp, n_comp) != n_comp )
{
free(comp);
return ( GBM_ERR_WRITE );
}
}
}
break;
/*...e*/
}
free(comp);
offset_end = lseek(fd, 0L, SEEK_CUR);
if ( (offset_end - offset_body) & 1 )
/* BODY is an odd # of bytes long! oops, better 'fix' this */
{
byte b;
if ( write(fd, &b, 1) != 1 )
return ( GBM_ERR_WRITE );
offset_end++;
}
put_dword(body + 4, offset_end - offset_body);
lseek(fd, offset_body - 4L, SEEK_SET);
if ( write(fd, body + 4, 4) != 4 )
return ( GBM_ERR_WRITE );
*end = offset_end;
return ( GBM_ERR_OK );
}
/*...e*/
static byte posdiff [0x100];
#define absdiff(a,b) posdiff[(byte)((a)-(b))]
/*...slbm_build_abstab:0:*/
static void lbm_build_abstab(void)
{
int i;
for ( i = 0; i < 0x80; i++ )
posdiff [i] = (byte) i;
for ( i = 1; i <= 0x80; i++ )
posdiff [0x100 - i] = (byte) i;
}
/*...e*/
/*...slbm_ham6:0:*/
/*
We will assume a simple greyscale palette, to make life easy.
Palette entries 0 to 15 will be 00, 11, 22, 33, ... ff.
Start the line with a close grey.
Try to get as many components right first time.
Then for each pixel, pick colours to get as many components right each go.
*/
static void lbm_ham6(byte *data, byte *ham, int n)
{
if ( n-- > 0 )
{
byte cb = (*data++ >> 4);
byte cg = (*data++ >> 4);
byte cr = (*data++ >> 4);
switch ( (cr==cg?4:0) + (cg==cb?2:0) + (cb==cr?1:0) )
{
/*...s0 \45\ none same:24:*/
case 0:
*ham++ = cr = cg = cb = ( cr + cg + cb ) / 3;
break;
/*...e*/
/*...s1 \45\ r\61\b:24:*/
case 1:
*ham++ = cg = cr;
break;
/*...e*/
/*...s2 \45\ g\61\b:24:*/
case 2:
*ham++ = cr = cg;
break;
/*...e*/
/*...s4 \45\ r\61\g:24:*/
case 4:
*ham++ = cb = cr;
break;
/*...e*/
/*...s7 \45\ r\61\g\61\b:24:*/
case 7:
*ham++ = cr;
break;
/*...e*/
}
while ( n-- > 0 )
{
byte b = (*data++ >> 4);
byte g = (*data++ >> 4);
byte r = (*data++ >> 4);
switch ( ((cr==r)?4:0) + ((cg==g)?2:0) + ((cb==b)?1:0) )
{
/*...s0 \45\ none same:32:*/
/*
3 colour components need modifying.
Can take upto 3 pixels to get the desired colour.
Picking a grey might fix up 2 or more colour components.
Pick r,g or b modify to get as close as possible.
*/
case 0:
if ( ((r==g)?1:0) + ((g==b)?1:0) + ((b==r)?1:0) > 0 )
{ *ham++ = cr = cg = cb = ( (r==g) ? r : b ); }
else if ( absdiff(cr,r) >= absdiff(cg,g) && absdiff(cr,r) >= absdiff(cb,b) )
{ *ham++ = (0x20 | r); cr = r; }
else if ( absdiff(cg,g) >= absdiff(cr,r) && absdiff(cg,g) >= absdiff(cb,b) )
{ *ham++ = (0x30 | g); cg = g; }
else
{ *ham++ = (0x10 | b); cb = b; }
break;
/*...e*/
/*...s1 \45\ b same:32:*/
/*
2 colour components need modifying.
Can take upto 2 pixels to get the desired colour.
Picking a grey might fix up both colour components.
Pick r or g modify to get as close as possible.
After this, we expect to have <= 1 colour components in error.
*/
case 1:
if ( r == g && g == b )
{ *ham++ = cr = cg = b; }
else if ( absdiff(cr,r) >= absdiff(cg,g) )
{ *ham++ = (0x20 | r); cr = r; }
else
{ *ham++ = (0x30 | g); cg = g; }
break;
/*...e*/
/*...s2 \45\ g same:32:*/
/*
2 colour components need modifying.
Can take upto 2 pixels to get the desired colour.
Picking a grey might fix up both colour components.
Pick r or b modify to get as close as possible.
After this, we expect to have <= 1 colour components in error.
*/
case 2:
if ( r == g && g == b )
{ *ham++ = cr = cb = g; }
else if ( absdiff(cr,r) >= absdiff(cb,b) )
{ *ham++ = (0x20 | r); cr = r; }
else
{ *ham++ = (0x10 | b); cb = b; }
break;
/*...e*/
/*...s3 \45\ g\44\b same:32:*/
case 3:
*ham++ = (0x20 | r); cr = r;
break;
/*...e*/
/*...s4 \45\ r same:32:*/
/*
2 colour components need modifying.
Can take upto 2 pixels to get the desired colour.
Picking a grey might fix up both colour components.
Pick g or b modify to get as close as possible.
After this, we expect to have <= 1 colour components in error.
*/
case 4:
if ( r == g && g == b )
{ *ham++ = cg = cb = r; }
else if ( absdiff(cg,g) >= absdiff(cb,b) )
{ *ham++ = (0x30 | g); cg = g; }
else
{ *ham++ = (0x10 | b); cb = b; }
break;
/*...e*/
/*...s5 \45\ r\44\b same:32:*/
case 5:
*ham++ = (0x30 | g); cg = g;
break;
/*...e*/
/*...s6 \45\ r\44\g same:32:*/
case 6:
*ham++ = (0x10 | b); cb = b;
break;
/*...e*/
/*...s7 \45\ r\44\g\44\b same:32:*/
case 7:
*ham++ = (0x10 | b);
break;
/*...e*/
}
}
}
}
/*...e*/
GBM_ERR lbm_w(char *fn, int fd, GBM *gbm, GBMRGB *gbmrgb, byte *data, char *opt)
{
byte formilbm [8+4];
long end;
GBM_ERR rc;
fn=fn; /* Suppress 'unref args' compiler warning */
memcpy(formilbm, "FORM", 4);
/* formilbm [4..7] will be filled in later */
memcpy(formilbm+8, "ILBM", 4);
if ( write(fd, formilbm, 8+4) != 8+4 )
return ( GBM_ERR_WRITE );
if ( gbm -> bpp == 24 )
/*...smap to ham6 write bmhd\44\ camg\44\ cmap and body:16:*/
{
byte *ham;
GBMRGB gbmrgb_grey [0x10];
int stride8 = ((gbm -> w + 3) & ~3);
int stride24 = ((gbm -> w * 3 + 3) & ~3);
int i;
if ( (rc = write_bmhd(fd, gbm, 6, opt)) != GBM_ERR_OK )
return ( rc );
if ( (rc = write_camg(fd, CAMG_HAM)) != GBM_ERR_OK )
return ( rc );
for ( i = 0; i <= 0x10; i++ )
gbmrgb_grey [i].r = gbmrgb_grey [i].g = gbmrgb_grey [i].b = (byte) (i * 0x11);
if ( (rc = write_cmap(fd, gbmrgb_grey, 4)) != GBM_ERR_OK )
return ( rc );
if ( (ham = malloc(stride8 * gbm -> h)) == NULL )
return ( GBM_ERR_MEM );
lbm_build_abstab();
for ( i = 0; i < gbm -> h; i++ )
lbm_ham6(data + i * stride24, ham + i * stride8, gbm -> w);
if ( (rc = write_body(fd, gbm, 8, 6, ham, &end)) != GBM_ERR_OK )
{
free(ham);
return ( rc );
}
free(ham);
}
/*...e*/
else
/*...swrite bmhd\44\ cmap and body:16:*/
{
if ( (rc = write_bmhd(fd, gbm, gbm -> bpp, opt)) != GBM_ERR_OK )
return ( rc );
if ( (rc = write_cmap(fd, gbmrgb, gbm -> bpp)) != GBM_ERR_OK )
return ( rc );
if ( (rc = write_body(fd, gbm, gbm -> bpp, gbm -> bpp, data, &end)) != GBM_ERR_OK )
return ( rc );
}
/*...e*/
put_dword(formilbm + 4, end - 8L);
lseek(fd, 4L, SEEK_SET);
if ( write(fd, formilbm + 4, 4) != 4 )
return ( GBM_ERR_WRITE );
return ( GBM_ERR_OK );
}
/*...e*/
/*...slbm_err:0:*/
char *lbm_err(GBM_ERR rc)
{
switch ( (int) rc )
{
case GBM_ERR_LBM_FORM:
return ( "no FORM signiture" );
case GBM_ERR_LBM_ILBM:
return ( "no ILBM signiture" );
case GBM_ERR_LBM_BMHD_2:
return ( "multiple BMHD bitmap headers" );
case GBM_ERR_LBM_BMHD_0:
return ( "no BMHD bitmap header" );
case GBM_ERR_LBM_BMHD_SIZE:
return ( "bad size of BMHD bitmap header" );
case GBM_ERR_LBM_BPP:
return ( "unsupported/bad bits per pixel" );
case GBM_ERR_LBM_CMAP_SIZE:
return ( "CMAP colour map is the wrong size" );
case GBM_ERR_LBM_COMP:
return ( "compression type not uncompressed nor RLE" );
case GBM_ERR_LBM_CAMG_SIZE:
return ( "CAMG chunk is the wrong size" );
case GBM_ERR_LBM_SHAM_VER:
return ( "Unsupported version of SHAM (not 0)" );
}
return ( NULL );
}
/*...e*/