/*
* in_lbm.cpp -- read a Deluxe Paint IFF/ILBM file
* by
[email protected] at Fri Mar 1 17:12:16 CET 2002
* -- Fri Mar 1 22:28:03 CET 2002
*
* I've ripped the reader code from xscavenger's source, and enhanced it
* a little bit (at Fri Mar 1 17:12:43 CET 2002), but I still think that
* this can read only the minority of the LBM files around.
*/
/* Imp: get docs about the LBM format, and rewrite this from scratch */
#ifdef __GNUC__
#ifndef __clang__
#pragma implementation
#endif
#endif
#include "image.hpp"
#include "error.hpp"
#if USE_IN_LBM
#include <string.h> /* memchr() */
#include "gensio.hpp"
#define FORM 0x464f524dL
#define ILBM 0x494c424dL
#define PBM 0x50424d20L
#define CMAP 0x434d4150L
#define BODY 0x424f4459L
#define BMHD 0x424d4844L
/* typedef unsigned char uchar; */
#define MAXBYTES 128 /* ?? */
/* --- */
#if SIZEOF_INT>=4
typedef unsigned int u32_t;
typedef signed int s32_t;
#else
typedef unsigned long u32_t;
typedef signed long s32_t;
#endif
class LBMRead {
public:
inline LBMRead(FILE *f_): f(f_) {}
/* inline ~LBMRead() { fclose(f); } */
Image::Sampled *doit();
protected:
inline int getcn();
void errUnEOF();
void err(char const*s);
u32_t getUnpack(unsigned len, char *where);
u32_t getu32();
unsigned getu16();
FILE *f;
};
//static inline int fixbyte(char *take,int bit,int depth) {
// int res=0,mask=1;
// while (depth--) {
// if (*take & bit) res|=mask;
// mask<<=1;
// take+=MAXBYTES;
// }
// return res;
//}
#if 0
static void debit(char *picput, char *lines, unsigned wd, unsigned depth) {
unsigned depth2, bit=128, res, i, mask;
unsigned byteswide=((wd+15)>>4)<<1;
unsigned char *take;
for (i=0;i<wd;i++) {
mask=1; depth2=depth; take=(unsigned char*)lines; res=0; while (depth2--!=0) {
if (0!=(*take&bit)) res|=mask;
mask<<=1;
take+=byteswide;
}
*picput++=res;
if (bit==1) { bit=128; ++lines; } else bit>>=1;
}
}
#endif
void LBMRead::errUnEOF() {
Error::sev(Error::EERROR) << "LBM: unexpected EOF" << (Error*)0;
}
void LBMRead::err(char const*s) {
Error::sev(Error::EERROR) << s << (Error*)0;
}
inline int LBMRead::getcn() {
int i=MACRO_GETC(f);
if (i==-1) errUnEOF();
return i;
}
u32_t LBMRead::getUnpack(unsigned len, char *where) {
/* RLE decompression */
int ch,ch2;
u32_t sofar=0;
while (len>0) {
ch=getcn();
if (ch<128) {
ch++;
assert(ch>=1);
if (len<(unsigned)ch) err("LBM: packet#1 too long");
len-=ch;
sofar+=1+ch;
while (ch--) *where++=getcn();
} else {
ch=1-(ch-256); /* 2..129 */
assert(ch>=1);
// fprintf(stderr, "len=%u ch=%u\n", len, ch);
if (len<(unsigned)ch) err("LBM: packet#2 too long");
len-=ch;
// fprintf(stderr, "xlen=%u ch=%u\n", len, ch);
ch2=getcn();
sofar+=2;
while (ch--) *where++=ch2;
}
}
return sofar;
}
u32_t LBMRead::getu32() {
u32_t val1=0;
val1=getcn()<<24L;
val1|=getcn()<<16L;
val1|=getcn()<<8;
val1|=getcn();
return val1;
}
unsigned LBMRead::getu16() {
u32_t val1=0;
val1|=getcn()<<8;
val1|=getcn();
return val1;
}
Image::Sampled *LBMRead::doit() {
char temparea[16];
u32_t type, size, i;
unsigned char comp=0;
unsigned j;
int ii;
Image::Sampled::dimen_t lbm_wd, lbm_ht, byteswide, depth=0; /* pts */
unsigned transp=256;
Image::Sampled *img=(Image::Sampled*)NULLP;
bool had_cmap=false, had_body=false, do_skip, had_transp=false;
/* sprintf(temparea,"%s/%s/%s",localname,localdirname,name); */
for (i=0;i<12;i++) temparea[i]=getcn();
if (0!=memcmp(temparea,"FORM",4) ||
0!=memcmp(temparea+8,"ILBM",4)) err("LBM: magic says: this is not an LBM");
lbm_wd=lbm_ht=byteswide=0;
while ((ii=MACRO_GETC(f))!=-1 && ii!='\0') {
ungetc(ii,f);
if ((type=getu32())==0xffffffffL) break;
size=getu32();
do_skip=(size&1); /* pad to even size */
// fprintf(stderr,"size=%u\n", size);
if (type==BMHD) {
if (img!=NULLP) err("LBM: duplicate BMHD");
// if (size>sizeof(temparea)) {
// for (i=0;i<sizeof(temparea);i++) temparea[i]=getcn();
// for (;i<size;i++) getcn();
// } else for (i=0;i<size;i++) temparea[i]=getcn();
if (size<20) errUnEOF();
lbm_wd=getu16();
lbm_ht=getu16();
if (getu32()!=0) err("LBM: expected offsets==0");
depth=getcn();
if (depth!=24 && (depth<1 || depth>8)) err("LBM: invalid color depth");
if (getcn()!=0) err("LBM: expected masking==0");
if ((comp=getcn())>=2) err("LBM: expected comp==0 || comp==1");
getcn(); /* ignore padding */
transp=getu16(); /* index of transparent color */
// fprintf(stderr, "htt=%u transp=%u\n", lbm_ht, transp);
getu16(); /* Ignore aspect ratio. */
if (lbm_wd!=getu16() || lbm_ht!=getu16()) err("LBM: inconsistent dimens");
/* ^^^ Dat: pagex, pagey */
byteswide=((lbm_wd+15)>>4)<<1;
/*printf("(%d,%d) %d bpp\n",lbm_wd,lbm_ht,depth);*/
if (depth==24) {
had_cmap=true;
img=new Image::RGB(lbm_wd, lbm_ht, 8);
} else {
img=new Image::Indexed(lbm_wd, lbm_ht, 1<<depth, 8);
memset(img->getHeadp(), '\0', 3*(1<<depth)); /* clear palette */
}
} else if (type==BODY) {
if (!had_cmap) err("LBM: should be CMAP, BODY");
// fprintf(stderr, "wd=%u, %u exp=%u\n", lbm_wd, size, byteswide*depth*lbm_ht);
if (comp==0 && size!=byteswide*depth*lbm_ht) err("LBM: wrong BODY length");
char *buf=new char[byteswide*depth], *picput=img->getRowbeg(); // const_cast<char*>(img->getCString());
for (i=0;i<lbm_ht;i++) {
// fprintf(stderr, "i=%u ht=%u\n", i, lbm_ht);
if (comp) { for (j=0;j<depth;j++) size-=getUnpack(byteswide, buf+byteswide*j); }
else { for (j=0;j<depth*byteswide;j++) buf[j]=getcn(); }
/* ^^^ Imp: test !comp: find uncompressed LBM files */
if ((s32_t)size<0) goto dtl;
/* debit() */
/* LBM compression is quite stupid: it run-length-compresses
* individiual bits of the color depth as 8-bit bytes. Plus it requires
* each bit vector to be aligned to 16-bit boundary.
*/
Image::Sampled::dimen_t depth2, bit=128, res, i, mask;
// unsigned byteswide=((lbm_wd+15)>>4)<<1;
unsigned char *take, *lines=(unsigned char*)buf;
if (depth!=24) {
for (i=0;i<lbm_wd;i++) {
mask=1; depth2=depth; take=lines; res=0; while (depth2--!=0) {
if (0!=(*take&bit)) res|=mask;
mask<<=1;
take+=byteswide;
}
if (res==transp) had_transp=true;
*picput++=res;
if (bit==1) { bit=128; ++lines; } else bit>>=1;
}
} else {
u32_t res;
for (i=0;i<lbm_wd;i++) {
mask=1; depth2=24; take=lines; res=0; while (depth2--!=0) {
if (0!=(*take&bit)) res|=mask;
mask<<=1;
take+=byteswide;
}
/* No transparency here. */
*picput++=res; /* Shift order verified. */
*picput++=res>>8;
*picput++=res>>16;
if (bit==1) { bit=128; ++lines; } else bit>>=1;
}
}
}
// fprintf(stderr,"size=%u\n", size);
if (size>1) { dtl: err("LBM: data too long"); }
if (size==1) getcn();
if (had_transp) { assert(depth!=24); static_cast<Image::Indexed*>(img)->setTransp(transp); }
// img->getRowbeg()[0]='\001'; img->getRowbeg()[1]='\002';
delete [] buf;
had_body=true;
} else if (type==CMAP) {
if (img==NULLP) err("LBM: should be: BMHD, CMAP");
if (had_cmap) err("LBM: duplicate CMAP");
if (size>3*(1U<<depth)) err("LBM: CMAP too large");
char *colormap=img->getHeadp();
// fprintf(stderr,"Si=%d size=%u\n", img->getRowbeg()-colormap, size);
for (i=0;i<size;i++) colormap[i]=getcn();
had_cmap=true;
} else {
Error::sev(Error::WARNING) << "LBM: unknown tag ignored: " << type << (Error*)0;
while (size--) MACRO_GETC(f);
}
if (do_skip) getcn();
// fprintf(stderr,"do_skip=%d t=%lu\n", do_skip, ftell(f));
}
if (!had_body) err("LBM: missing BODY");
return img;
}
static Image::Sampled *in_lbm_reader(Image::Loader::UFD *ufd, SimBuffer::Flat const&) {
return LBMRead(((Filter::UngetFILED*)ufd)->getFILE(/*seekable:*/false)).doit();
/* ^^^ Destructor: fclose((FILE*)file_); */
}
static Image::Loader::reader_t in_lbm_checker(char buf[Image::Loader::MAGIC_LEN], char [Image::Loader::MAGIC_LEN], SimBuffer::Flat const&, Image::Loader::UFD*) {
return (0==memcmp(buf,"FORM",4) && 0==memcmp(buf+8,"ILBM",4)) ? in_lbm_reader : 0;
}
#else
#define in_lbm_checker NULLP
#endif /* USE_IN_XPM */
Image::Loader in_lbm_loader = { "LBM", in_lbm_checker, 0 };