/* VIEW.C - tiny ILBM viewer; (c) 1986 DJH Note that there are TWO ways to call this program; from CLI, as in : "View filespec" or from WorkBench. If spawned from WorkBench via a tool icon, its ToolTypes array is searched for a "PICTURE=fspec" argument detailing the file to display. */ extern struct WBStartup *WBenchMsg; #define TITLE "View 1.8 : (c) 1986-87 John Hodgson\n" #define MAXWIDTH 376 /* max non-HIRES width */ #define MAXHEIGHT 242 /* max non-interlaced height */ #define MAXCOLORS 32 /* max # colors supported */ #define MakeID(a,b,c,d) ((a)<<24L | (b)<<16L | (c)<<8 | (d)) #define ID_FORM MakeID('F','O','R','M') #define ID_ILBM MakeID('I','L','B','M') #define ID_BMHD MakeID('B','M','H','D') #define ID_CAMG MakeID('C','A','M','G') #define ID_CMAP MakeID('C','M','A','P') #define ID_BODY MakeID('B','O','D','Y') #define cmpByteRun1 1 #define ROUNDODDUP(a) (((a)+1)&(~1L)) typedef struct { long ckID,ckSize; } Chunk; typedef struct { short w,h,x,y; char nPlanes,masking,compression,pad1; short transparentColor; char xAspect, yAspect; short pageWidth,pageHeight; } BitMapHeader; #define SafeRead(a,b,c) if (Read(a,b,c)==-1L) { Close(a); return(NULL); } void *IntuitionBase,*GfxBase,*IconBase; /************************************************************************ * * * Routine name(s) : ReadILBM() * * Author : D. John Hodgson * * Environment : Aztec "C", default * * * * ReadILBM attempts to read an IFF file and display it on a freshly * * opened custom screen. Returns a screen pointer if sucessful, * * otherwise NULL. * * * * LIMITATIONS : no masking, CATS/LISTS/PROPS. CAMG chunks supported. * ************************************************************************/ BitMapHeader bmhd; char *bufstart; Chunk header; void *ReadILBM(fspec) char *fspec; /* AmigaDOS filename */ { struct NewScreen NewScreen; struct Screen *screen; struct FileHandle *fp; char colormap[MAXCOLORS][3],*sourcebuf; short colorcount; long id,ViewModes=0; if ((fp=Open(fspec,MODE_OLDFILE))==0) return(NULL); SafeRead(fp,&header,(long)sizeof(header)); if (header.ckID!=ID_FORM) { Close(fp); return(NULL); } SafeRead(fp,&id,(long)sizeof(id)); if (id!=ID_ILBM) { Close(fp); return(NULL); } for (;;) { SafeRead(fp,&header,(long)sizeof(header)); if (header.ckID==ID_BODY) break; switch(header.ckID) { case ID_BMHD: SafeRead(fp,&bmhd,(long)sizeof(bmhd)); break; case ID_CMAP: SafeRead(fp,&colormap[0][0],(long)header.ckSize); colorcount=header.ckSize/3; break; case ID_CAMG: SafeRead(fp,&ViewModes,(long)header.ckSize); break; default: Seek(fp,ROUNDODDUP(header.ckSize),OFFSET_CURRENT); } } /* Read planes into RAM for ease if decompression */ sourcebuf=bufstart=AllocMem((long)header.ckSize,MEMF_PUBLIC); if (sourcebuf==0L) puts("Error allocating memory!"); SafeRead(fp,sourcebuf,(long)header.ckSize); Close(fp); NewScreen.LeftEdge=0; NewScreen.TopEdge=0; NewScreen.Width=bmhd.w; NewScreen.Height=bmhd.h; NewScreen.Depth=bmhd.nPlanes; /* make some forced assumptions if CAMG chunk unavailable */ if (!(NewScreen.ViewModes=ViewModes)) { if (bmhd.w>MAXWIDTH) NewScreen.ViewModes|=HIRES; if (bmhd.h>MAXHEIGHT) NewScreen.ViewModes|=LACE; } NewScreen.Type=CUSTOMSCREEN; NewScreen.Font=0L; NewScreen.Gadgets=0L; screen=OpenScreen(&NewScreen); while (colorcount--) SetRGB4(&screen->ViewPort,(long)colorcount, colormap[colorcount][0]>>4L,colormap[colorcount][1]>>4L, colormap[colorcount][2]>>4L); return(screen); } Expand(screen,bmhd,sourcebuf) /* Fast line decompress/deinterleave */ struct Screen *screen; BitMapHeader *bmhd; register char *sourcebuf; { register char n,*destbuf; /* in order of preferred allocation */ register short plane,linelen,rowbytes,i; linelen=bmhd->w/8; for (i=0;ih;i++) /* process n lines/screen */ for (plane=0;planenPlanes;plane++) { /* process n planes/line */ destbuf=(char *)(screen->BitMap.Planes[plane])+linelen*i; if (bmhd->compression==cmpByteRun1) { /* compressed screen? */ rowbytes=linelen; while (rowbytes) { /* unpack until 1 scan-line complete */ n=*sourcebuf++; /* fetch block run marker */ /* uncompressed block? copy n bytes verbatim */ if (n>=0) { movmem(sourcebuf,destbuf,(unsigned int)++n); rowbytes-=n; destbuf+=n; sourcebuf+=n; } else { /* compressed block? expand n duplicate bytes */ n=-n+1; rowbytes-=n; setmem(destbuf,(unsigned int)n,(unsigned int)*sourcebuf++); destbuf+=n; } } /* finish unpacking line */ } else { /* uncompressed? just copy */ movmem(sourcebuf,destbuf,(unsigned int)linelen); sourcebuf+=linelen; destbuf+=linelen; } } /* finish interleaved planes, lines */ } struct Screen *ParseWBArgs() /* analyze WB arguments */ { struct DiskObject *diskobj; struct Screen *screen; char *toolarg; /* scrutinize parent icon to fetch ToolTypes */ if (!(diskobj=GetDiskObject(WBenchMsg->sm_ArgList->wa_Name))) return(0); /* scan ToolTypes for a "PICTURE=filespec" argument */ if (!(toolarg=FindToolType(diskobj->do_ToolTypes,"PICTURE"))) { FreeDiskObject(diskobj); return(0); } screen=ReadILBM(toolarg); /* parse ILBM & open screen */ FreeDiskObject(diskobj); return(screen); } main(argc,argv) int argc; char *argv[]; { struct Screen *screen; struct Window *window; struct NewWindow nw; short i; IntuitionBase=OpenLibrary("intuition.library",0L); GfxBase=OpenLibrary("graphics.library",0L); IconBase=OpenLibrary("icon.library",0L); if (argc==0) { if (!(screen=ParseWBArgs())) exit(100); } /* son of WB? */ else { Write(Output(),TITLE,(long)sizeof(TITLE)); /* CLI title */ if (!(screen=ReadILBM(argv[1]))) exit(100); } /* a note on window selection; the ONLY reason we're opening a window AT ALL is to get the LMB event so the user can close the screen. Any window that : 1) eats up as little RAM as possible, 2) renders absolutely nothing, 3) covers the screen title bar will do. Note that we can prevent anything from displaying (even screen title bars) by leaving DetailPen & BlockPens at zero; rendering a background color on an empty screen displays nothing! Note that we render DIRECTLY (via Expand()) into the screen's BitMap; This saves us from allocating another buffer just so that we can copy it into the window itself. Sure this scribbles over any window/screen imagery, but we're not displaying any anyway. Nyaah. */ setmem(&nw,sizeof(nw),0); /* start w/fresh structure */ nw.IDCMPFlags=MOUSEBUTTONS; nw.Flags=SIMPLE_REFRESH|BORDERLESS; nw.Screen=screen; nw.Type=CUSTOMSCREEN; nw.Height=bmhd.h; nw.Width=bmhd.w; if (!(window=OpenWindow(&nw))) { CloseScreen(screen); exit(100); } Expand(screen,&bmhd,bufstart); FreeMem(bufstart,(long)header.ckSize); /* free compressed buffer */ WaitPort(window->UserPort); /* wait for a LMB */ CloseWindow(window); /* Intuition will ReplyMsg() for us */ CloseScreen(screen); CloseLibrary(IntuitionBase); CloseLibrary(GfxBase); CloseLibrary(IconBase); }