1 /* Modified version for AROS - The AROS Research OS
5 /***********************************************************************\
7 * A simple, fast ILBM viewer, for use under AmigaOS V2.x. *
8 * Written and ©1991-1992 by Dave Schreiber. All Rights Reserved. *
11 * 2View FILE/A/M,FROM/K,SECS=SECONDS/K/N,TICKS/K/N,LOOP/S,PRINT *
13 * Where the following arguments are defined as follows: *
14 * FILE - The name of one (or more) IFF ILBM files *
15 * FROM - A file containing a list of filenames. Used instead of FILE *
16 * SECS - Number of seconds to display a file *
17 * TICKS - Number of ticks (1/60ths of a second) *
18 * LOOP - When finished showing the last pictures, start over *
19 * PRINT - Print each picture as it is shown *
21 * To compile (with SAS/C V5.10a): *
25 * blink with 2View.lnk *
28 * 1.50 - Rewrote the subroutine that reads the ILBM from disk in *
29 * assembly language, for speed. Added support for SHAM and *
30 * Macro Paint images, and for color cycling (both *
31 * traditional (CRNG, i.e. continutout cycle ranges) and *
32 * DPaint-IV style cycling (DRNG, i.e. non-continuous cycle *
33 * ranges). A 'tick' (as used with the "TICK" keyword, above)*
34 * has been redefined as 1/60th of a second. Finally, the *
35 * source code in 2View.c has been split into two files *
36 * (2View.c and Misc.c). *
39 * 1.11 - Improved error reporting (with this version, if the user *
40 * run 2View from Workbench and there's an error, a requester *
41 * is put up. Previously, the user was not notified at all *
45 * 1.10 - Added support for Workbench, ARexx, scrollable bitmaps, *
46 * and printing. Also, the user can now use CTRL-C to advance*
47 * to the next frame, and CTRL-D to abort a playlist. *
50 * 1.00 - Original version. Released 7/24/91 *
52 \************************************************************************/
55 unsigned long availBytes
,curPos
,bufSize
;
59 #include <exec/types.h>
60 #include <libraries/iffparse.h>
62 #include <dos/dosasl.h>
63 #include <intuition/intuition.h>
64 #include <exec/memory.h>
65 #include <workbench/startup.h>
66 #include <graphics/gfxbase.h>
69 #include <proto/exec.h>
70 #include <proto/intuition.h>
71 #include <proto/dos.h>
72 #include <proto/graphics.h>
73 #include <proto/iffparse.h>
75 /*Other include files*/
78 /* #include "arexx.h" */
83 /*Libraries we'll need*/
84 struct Library
*IFFParseBase
=NULL
;
85 struct IntuitionBase
*IntuitionBase
=NULL
;
86 struct GfxBase
*GfxBase
=NULL
;
92 /*Generic screen and window definitions. They will be used to define*/
93 /*the screen and window that the various pictures will be shown on*/
94 struct NewScreen newScreen
=
96 0,0,0,0,0,1,0,0,CUSTOMSCREEN
|SCREENBEHIND
|AUTOSCROLL
,NULL
,NULL
,NULL
,
100 struct NewWindow newWindow
=
102 0,0,0,0,0,1,IDCMP_MENUPICK
|IDCMP_MOUSEBUTTONS
|IDCMP_ACTIVEWINDOW
|IDCMP_VANILLAKEY
,
103 WFLG_RMBTRAP
|WFLG_BORDERLESS
|WFLG_NOCAREREFRESH
,NULL
,NULL
,NULL
,NULL
,NULL
,
104 0,0,640,400,CUSTOMSCREEN
107 struct Screen
*screen
=NULL
;
108 struct Window
*window
=NULL
;
113 /*A true here indicates the current ILBM file is compressed*/
116 /*The version string. Do a 'version 2View' to display it*/
117 char *version
="$VER: QView V1.50 (24.3.92)";
119 /*Just so that the © message is part of the actual program*/
120 char *copyRightMsg
="Copyright 1991-1992 by Dave Schreiber. All Rights Reserved.";
122 BYTE ExitFlag
=FALSE
; /*'Exit now' flag*/
123 UWORD ticks
=0; /*Delay requested by user.*/
125 /*The previous screen and window*/
126 struct Window
*prevWindow
=NULL
;
127 struct Screen
*prevScreen
=NULL
;
129 /*Data for a blank pointer*/
130 UWORD __chip fakePointerData
[]={0,0,0,0,0};
132 struct IFFHandle
*iff
=NULL
; /*IFF handle*/
133 BPTR pL
=NULL
; /*Playlist file pointer*/
134 BOOL masking
,print
,toFront
,printPics
;
136 struct WBStartup
*WBenchMsg
= NULL
;
138 extern struct WBStartup
*WBenchMsg
;
141 char *playListFilename
=NULL
;
143 /*Variables that have to be global so that ARexx.c can access them*/
144 ButtonTypes rexxAbort
=none
;
145 /* long arexxSigBit; */
146 UWORD ticksRemaining
=0;
151 char trashBuf
[512]; /* A place to dump mask information */
154 struct TagItem TagList
[]=
156 /* This defines what part of the displayed picture is shown. It's */
157 /* necessary to have a line like this in here in order to get */
158 /* 2.0 autoscrolling to work. */
159 {SA_Overscan
,OSCAN_VIDEO
},
163 char *about1
="2View";
164 char *about2
="Please";
166 extern struct EasyStruct erError1Line
;
170 UBYTE numCycleColors
;
173 /* The assembly-language reader routine */
174 extern int ReadILBM (struct IFFHandle
* iff
,
175 struct Window
* window
, ULONG width
, ULONG height
, UWORD Depth
,
176 BOOL Compression
, BOOL masking
);
179 UWORD numFilenames
=0,numSlots
;
181 int main(int argc
, char ** argv
)
184 IPTR args
[7] = { 0 };
185 char **filenames
= NULL
;
186 char curFilename
[140];
187 BYTE playList
= FALSE
; /*True if a playlist is being used, false otherwise*/
189 /*Open the libraries*/
190 IFFParseBase
=(struct Library
*)OpenLibrary("iffparse.library",0L);
191 if(IFFParseBase
==NULL
)
197 IntuitionBase
=(struct IntuitionBase
*)OpenLibrary("intuition.library",0L);
198 if(IntuitionBase
==NULL
)
204 GfxBase
=(struct GfxBase
*)OpenLibrary("graphics.library",0L);
211 /*Get the arguments*/
216 /*If a playlist filename was provided, store it for later use*/
217 if((char *)args
[4]!=NULL
)
219 playListFilename
=(char *)args
[4];
222 else /*Otherwise, read the filenames from the command line*/
225 /*If a time was provided (in ticks), use it*/
226 if((ULONG
*)args
[1]!=NULL
)
227 ticks
=*(ULONG
*)args
[1]*50;
229 /*If a time was provided (in seconds), use it (overrides ticks)*/
230 if((ULONG
*)args
[2]!=NULL
&& *(ULONG
*)args
[2]!=0)
231 ticks
=*(ULONG
*)args
[2];
233 /*If neither a picture filename, nor a playlist filename, was*/
234 /*specified, print an error and exit.*/
235 if((char **)args
[0]==NULL
&& !playList
)
237 printError("Please enter one or more filenames");
242 /*Determine if we should print the pictures we display or not*/
243 printPics
=((BOOL
*)args
[5]!=NULL
);
245 /*Get the pointer to the list of filename*/
246 filenames
=(char **)args
[0];
248 /*Will we loop back to the beginning once we finish displaying all*/
250 loop
=((BOOL
*)args
[3]!=NULL
);
253 if(WBenchMsg
->sm_NumArgs
==1)
255 EasyRequest(NULL
,&erError1Line
,NULL
,
256 (ULONG
) "2View V1.50 (March 24, 1992)",
257 (ULONG
) "Written by Dave Schreiber");
264 /* Initialize the ARexx port */
265 arexxSigBit
=initRexxPort();
273 /*Allocate the IFF structure*/
276 /*If the allocation failed, abort*/
279 printError("Couldn't allocate necessary resources");
284 /*Run until we run out of filenames, or the user aborts*/
287 picFilename
=curFilename
; /*Get a pointer to the filename buffer*/
289 /*Check to see if we're running from Workbench. If so, and the*/
290 /*user provided names of pictures to display (by clicking on their*/
291 /*icons), display those pictures*/
292 if(WBenchMsg
!=NULL
&& WBenchMsg
->sm_NumArgs
>1)
294 CurrentDir(WBenchMsg
->sm_ArgList
[1].wa_Lock
);
295 picFilename
=WBenchMsg
->sm_ArgList
[1].wa_Name
;
297 else if(playList
) /*If a playlist is being used*/
299 pL
=Open(playListFilename
,MODE_OLDFILE
); /*Open the playlist*/
301 if(pL
==NULL
) /*If we couldn't open the playlist, abort*/
303 printError("Can't open playlist");
308 do /*Loop until we run out of playlist, or get a valid name*/
310 if(FGets(pL
,picFilename
,140)==NULL
) /*If end-of-file*/
311 picFilename
=NULL
; /*Set as NULL as a flag*/
313 while(picFilename
!=NULL
&& picFilename
[0]==0x0A);
315 if(picFilename
!=NULL
) /*If not NULL, it's a valid filename*/
316 picFilename
[strlen(picFilename
)-1]='\0'; /*Remove the linefeed*/
318 else /*Otherwise, if a playlist isn't being used, get the current*/
319 picFilename
=filenames
[0]; /*filename*/
322 /*Loop while the user hasn't requested an abort, and while*/
323 /*there are still files to display*/
324 for(c
=0;!ExitFlag
&& picFilename
!=NULL
;c
++)
326 if((iff
->iff_Stream
=(IPTR
)Open(picFilename
,MODE_OLDFILE
))==0)
327 { /*If the ILBM file can't be opened...*/
329 /*Print an error...*/
330 printError("Can't open: %s", picFilename
);
336 InitIFFasDOS(iff
); /*The IFF file will be read from disk*/
338 OpenIFF(iff
,IFFF_READ
); /*Make iffparse.library aware of the*/
341 /*Read in the file and display*/
342 ReadAndDisplay(picFilename
,iff
);
344 CloseIFF(iff
); /*Release iffparse's hold on the file*/
346 Close((BPTR
)iff
->iff_Stream
); /*Close the file*/
348 /*Get the next filename, either from Workbench,*/
351 if(WBenchMsg
->sm_NumArgs
> c
+2)
353 CurrentDir(WBenchMsg
->sm_ArgList
[c
+2].wa_Lock
);
354 picFilename
=WBenchMsg
->sm_ArgList
[c
+2].wa_Name
;
359 else if(playList
) /*The playlist*/
363 if(FGets(pL
,picFilename
,140)==NULL
)
366 while(picFilename
!=NULL
&& picFilename
[0]==0x0A);
368 if(picFilename
!=NULL
)
369 picFilename
[strlen(picFilename
)-1]='\0';
371 else /*or the command line*/
372 picFilename
=filenames
[c
+1];
375 /*We're finished with this run of pictures*/
376 if(playList
) /*Close playlist, if open*/
380 if(!loop
&& !printPics
) /*If the loop flag wasn't given, exit*/
384 /*Time to exit, so close stuff*/
387 exit(0); /*And exit*/
390 LONG ilbmprops
[] = { ID_ILBM
,ID_CMAP
,ID_ILBM
,ID_BMHD
,ID_ILBM
,ID_CAMG
,
391 ID_ILBM
,ID_CRNG
,ID_ILBM
,ID_DRNG
,ID_ILBM
,ID_SHAM
,
394 /*Read in an ILBM file and display it*/
395 void ReadAndDisplay(char *filename
,struct IFFHandle
*iff
)
398 UBYTE
*bodyBuffer
= NULL
; /*Pointer to buffer holding 'BODY' chunk info*/
399 ULONG ViewModes
; /*Holds the viewmodes flags*/
402 UBYTE cycleTable
[32];
405 /*Structures required for IFF parsing*/
406 struct StoredProperty
*bmhd
,*cmap
,*camg
,*crng
,*drng
,*sham
,*ctbl
;
407 struct ContextNode
*bodyContext
;
410 struct IntuiMessage
*mesg
;
412 /*Indentify chunks that should be stored during parse*/
413 /*(in this case, CMAP, BMHD, CRNG, DRNG, CAMG, and SHAM)*/
414 error
=PropChunks(iff
,ilbmprops
,7);
416 /*If there was an error, print a message and return*/
419 printError("Error in PropChunks() wile reading %s: %d\n",filename
,error
);
424 /*Tell iffparse to stop at a 'BODY' chunk*/
425 error
=StopChunk(iff
,ID_ILBM
,ID_BODY
);
427 /*Error handling yet again*/
428 if(error
!=0 && error
!=-1)
430 printError("Error in StopChunk() wile reading %s: %d\n",filename
,error
);
435 /*Do the actual parsing*/
436 error
=ParseIFF(iff
,IFFPARSE_SCAN
);
438 /*Check for errors yet again*/
439 if(error
!=0 && error
!=-1)
441 printError("Error in ParseIFF() wile reading %s: %d\n",filename
,error
);
446 /*Get the chunks that were found in the file*/
447 bmhd
= FindProp(iff
,ID_ILBM
,ID_BMHD
); /*Bitmap information*/
448 cmap
= FindProp(iff
,ID_ILBM
,ID_CMAP
); /*Color map*/
449 camg
= FindProp(iff
,ID_ILBM
,ID_CAMG
); /*Amiga viewmode information*/
450 crng
= FindProp(iff
,ID_ILBM
,ID_CRNG
); /*Color-cycling ranges*/
451 drng
= FindProp(iff
,ID_ILBM
,ID_DRNG
); /*New (DPaint IV) color-cycling*/
452 sham
= FindProp(iff
,ID_ILBM
,ID_SHAM
); /*SHAM color tables*/
453 ctbl
= FindProp(iff
,ID_ILBM
,ID_CTBL
); /*Macro Paint color table info*/
455 /*Get the descriptor for the BODY chunk*/
456 bodyContext
=CurrentChunk(iff
);
458 /*If there wasn't a BMHD, CMAP, or BODY chunk, abort*/
459 if (!bmhd
| !cmap
| !bodyContext
)
461 printError ("%s is corrupted or is not in Amiga ILBM format. No %s%s%s%s%s found."
464 , !bmhd
&& !cmap
? ", " : ""
466 , (!bmhd
|| !cmap
) && !bodyContext
? ", " : ""
467 , bodyContext
? "" : "BODY"
473 /*Prepare to determine screen modes*/
474 newScreen
.ViewModes
=0;
476 /*If there was a CAMG chunk, use it to get the viewmodes*/
479 ViewModes
=( ((CAMG
*)(camg
->sp_Data
))->viewmodes
);
482 newScreen
.ViewModes
|=HAM
;
484 if(ViewModes
& EXTRA_HALFBRITE
)
485 newScreen
.ViewModes
|=EXTRA_HALFBRITE
;
488 newScreen
.ViewModes
|=LACE
;
490 if(ViewModes
& HIRES
)
491 newScreen
.ViewModes
|=HIRES
;
497 if(drng
==NULL
) /*No color cycling*/
499 else /* DPaint-IV--style color cycling*/
500 numCycleColors
=interpretDRNG(cycleTable
,(DRNG
*)(drng
->sp_Data
),&rate
);
501 } else /*DPaint I-III--style color cycling*/
502 numCycleColors
=interpretCRNG( cycleTable
,(CRNG
*)(crng
->sp_Data
),&rate
);
504 if(numCycleColors
!= 0)
505 cycle
=TRUE
; /*Start cycling if there are colors to cycle*/
509 /*Interpret the BMHD chunk*/
510 getBMHD(bmhd
->sp_Data
);
512 /*Don't open an interlace screen if the image is in SHAM mode*/
513 /*(the Amiga OS doesn't properly handle user copper lists on */
514 /*interlaced screens for some reason)*/
516 newScreen
.ViewModes
&=~LACE
;
518 /*Open a screen, defined by the BMHD and CAMG chunks*/
519 screen
=OpenScreenTagList(&newScreen
,TagList
);
521 /*If the screen couldn't be opened, abort*/
524 printError("Cannot open screen!");
529 /*This more properly centers the screen, for some reason */
530 MoveScreen(screen
,1,1);
531 MoveScreen(screen
,-1,-1);
533 /*Set the window dimensions from the screen dimensions*/
534 newWindow
.Screen
=screen
;
535 newWindow
.Width
=newScreen
.Width
;
536 newWindow
.Height
=newScreen
.Height
;
539 window
=OpenWindow(&newWindow
);
541 /*Abort if the window couldn't be opened*/
544 printError("Cannot open window!");
550 availBytes
= bufSize
;
553 /*Blank out the pointer*/
554 SetPointer(window
,fakePointerData
,1,16,0,0);
556 /*Set the screen colors to those provided in the CMAP chunk*/
557 setScreenColors(screen
,cmap
->sp_Data
,newScreen
.Depth
,destMap
,&numColors
);
559 /*Uncompress an ILBM and copy it into the bitmap*/
560 if (!ReadILBM (iff
, window
, window
->Width
, window
->Height
,
562 Compression
, masking
))
564 printError ("Cannot read bitmap!");
570 /*Activate the window, and flush any IDCMP message*/
571 ActivateWindow(window
);
572 while((mesg
=(struct IntuiMessage
*)GetMsg(window
->UserPort
))!=NULL
)
573 ReplyMsg((struct Message
*)mesg
);
576 /*If this is a SHAM image, setup the copper list appropriately*/
580 setupSHAM(screen
,(UWORD
*)(sham
->sp_Data
));
583 /*If this is a MacroPaint image, setup the copper list*/
586 specialModes
=MACROPAINT
;
587 setupDynHires(screen
,(UWORD
*)(ctbl
->sp_Data
));
591 /* Otherwise, this is a normal ILBM*/
592 specialModes
=NORMAL_MODE
;
594 /*Bring the screen to the front*/
595 ScreenToFront(screen
);
597 /*If the user used the 'print' flag on the command line, print*/
598 /*the picture (but not if this is a SHAM or MacroPaint image)*/
599 if(printPics
&& specialModes
== NORMAL_MODE
)
600 dumpRastPort(&(screen
->RastPort
),&(screen
->ViewPort
));
604 /*Close the previous window and screen*/
606 CloseWindow(prevWindow
);
608 CloseScreen(prevScreen
);
610 /*Free the buffer that holds the BODY chunk information*/
611 FreeMem(bodyBuffer
,bufSize
);
613 /*Store the current window & screen structures, so they can be*/
623 if(ticks
==0) /*If ticks==0, this means that no delay was specified*/
624 { /*by the user. So just wait for him to click a button*/
626 int prevTopEdge
=prevScreen
->TopEdge
;
629 while((button
=checkButton())==none
&& rexxAbort
==none
)
631 /*Wait for 1/60th of a second*/
632 Wait (1L << prevWindow
->UserPort
->mp_SigBit
);
635 /*Refresh the SHAM copper list if required*/
636 if(prevTopEdge
!=prevScreen
->TopEdge
&& sham
!=NULL
)
638 prevTopEdge
=prevScreen
->TopEdge
;
639 setupSHAM(prevScreen
,(UWORD
*)(sham
->sp_Data
));
642 /*Refresh the MacroPaint copper list if required*/
643 if(prevTopEdge
!=prevScreen
->TopEdge
&& ctbl
!=NULL
)
645 prevTopEdge
=prevScreen
->TopEdge
;
646 setupDynHires(prevScreen
,(UWORD
*)(ctbl
->sp_Data
));
650 /*If its time to cycle the colors, then cycle them*/
651 if(cycle
&& numCycleColors
!=0 && --countDown
==0)
653 cycleColors(cycleTable
,destMap
,numCycleColors
,numColors
);
662 /*Check to see if the user wants to abort*/
663 if(button
==menu
|| rexxAbort
==menu
)
666 else /*Otherwise, wait for the specified amount of time*/
670 /*Wait 1/60th of a second*/
673 /*Cycle colors if necessary*/
674 if(cycle
&& numCycleColors
!=0 && --countDown
==0)
676 cycleColors(cycleTable
,destMap
,numCycleColors
,numColors
);
681 dispRexxPort(); /*Check ARexx port*/
684 button
=checkButton(); /*After each 25 ticks, check to see if*/
685 if(button
==menu
|| rexxAbort
==menu
) /*the user wants to abort*/
690 if(button
==select
|| rexxAbort
==select
) /*Or advance prematurely*/