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
=BNULL
; /*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 (IPTR
) "2View V1.50 (March 24, 1992)",
257 (IPTR
) "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
==BNULL
) /*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
;
407 #if 0 /* for MacroPaint */
408 struct StoredProperty
*ctbl
;
410 struct ContextNode
*bodyContext
;
413 struct IntuiMessage
*mesg
;
415 /*Indentify chunks that should be stored during parse*/
416 /*(in this case, CMAP, BMHD, CRNG, DRNG, CAMG, and SHAM)*/
417 error
=PropChunks(iff
,ilbmprops
,7);
419 /*If there was an error, print a message and return*/
422 printError("Error in PropChunks() wile reading %s: %d\n",filename
,error
);
427 /*Tell iffparse to stop at a 'BODY' chunk*/
428 error
=StopChunk(iff
,ID_ILBM
,ID_BODY
);
430 /*Error handling yet again*/
431 if(error
!=0 && error
!=-1)
433 printError("Error in StopChunk() wile reading %s: %d\n",filename
,error
);
438 /*Do the actual parsing*/
439 error
=ParseIFF(iff
,IFFPARSE_SCAN
);
441 /*Check for errors yet again*/
442 if(error
!=0 && error
!=-1)
444 printError("Error in ParseIFF() wile reading %s: %d\n",filename
,error
);
449 /*Get the chunks that were found in the file*/
450 bmhd
= FindProp(iff
,ID_ILBM
,ID_BMHD
); /*Bitmap information*/
451 cmap
= FindProp(iff
,ID_ILBM
,ID_CMAP
); /*Color map*/
452 camg
= FindProp(iff
,ID_ILBM
,ID_CAMG
); /*Amiga viewmode information*/
453 crng
= FindProp(iff
,ID_ILBM
,ID_CRNG
); /*Color-cycling ranges*/
454 drng
= FindProp(iff
,ID_ILBM
,ID_DRNG
); /*New (DPaint IV) color-cycling*/
455 sham
= FindProp(iff
,ID_ILBM
,ID_SHAM
); /*SHAM color tables*/
457 ctbl
= FindProp(iff
,ID_ILBM
,ID_CTBL
); /*Macro Paint color table info*/
460 /*Get the descriptor for the BODY chunk*/
461 bodyContext
=CurrentChunk(iff
);
463 /*If there wasn't a BMHD, CMAP, or BODY chunk, abort*/
464 if (!bmhd
| !cmap
| !bodyContext
)
466 printError ("%s is corrupted or is not in Amiga ILBM format. No %s%s%s%s%s found."
469 , !bmhd
&& !cmap
? ", " : ""
471 , (!bmhd
|| !cmap
) && !bodyContext
? ", " : ""
472 , bodyContext
? "" : "BODY"
478 /*Prepare to determine screen modes*/
479 newScreen
.ViewModes
=0;
481 /*If there was a CAMG chunk, use it to get the viewmodes*/
484 ViewModes
=( ((CAMG
*)(camg
->sp_Data
))->viewmodes
);
487 newScreen
.ViewModes
|=HAM
;
489 if(ViewModes
& EXTRA_HALFBRITE
)
490 newScreen
.ViewModes
|=EXTRA_HALFBRITE
;
493 newScreen
.ViewModes
|=LACE
;
495 if(ViewModes
& HIRES
)
496 newScreen
.ViewModes
|=HIRES
;
502 if(drng
==NULL
) /*No color cycling*/
504 else /* DPaint-IV--style color cycling*/
505 numCycleColors
=interpretDRNG(cycleTable
,(DRNG
*)(drng
->sp_Data
),&rate
);
506 } else /*DPaint I-III--style color cycling*/
507 numCycleColors
=interpretCRNG( cycleTable
,(CRNG
*)(crng
->sp_Data
),&rate
);
509 if(numCycleColors
!= 0)
510 cycle
=TRUE
; /*Start cycling if there are colors to cycle*/
514 /*Interpret the BMHD chunk*/
515 getBMHD(bmhd
->sp_Data
);
517 /*Don't open an interlace screen if the image is in SHAM mode*/
518 /*(the Amiga OS doesn't properly handle user copper lists on */
519 /*interlaced screens for some reason)*/
521 newScreen
.ViewModes
&=~LACE
;
523 /*Open a screen, defined by the BMHD and CAMG chunks*/
524 screen
=OpenScreenTagList(&newScreen
,TagList
);
526 /*If the screen couldn't be opened, abort*/
529 printError("Cannot open screen!");
534 /*This more properly centers the screen, for some reason */
535 MoveScreen(screen
,1,1);
536 MoveScreen(screen
,-1,-1);
538 /*Set the window dimensions from the screen dimensions*/
539 newWindow
.Screen
=screen
;
540 newWindow
.Width
=newScreen
.Width
;
541 newWindow
.Height
=newScreen
.Height
;
544 window
=OpenWindow(&newWindow
);
546 /*Abort if the window couldn't be opened*/
549 printError("Cannot open window!");
555 availBytes
= bufSize
;
558 /*Blank out the pointer*/
559 SetPointer(window
,fakePointerData
,1,16,0,0);
561 /*Set the screen colors to those provided in the CMAP chunk*/
562 setScreenColors(screen
,cmap
->sp_Data
,newScreen
.Depth
,destMap
,&numColors
);
564 /*Uncompress an ILBM and copy it into the bitmap*/
565 if (!ReadILBM (iff
, window
, window
->Width
, window
->Height
,
567 Compression
, masking
))
569 printError ("Cannot read bitmap!");
575 /*Activate the window, and flush any IDCMP message*/
576 ActivateWindow(window
);
577 while((mesg
=(struct IntuiMessage
*)GetMsg(window
->UserPort
))!=NULL
)
578 ReplyMsg((struct Message
*)mesg
);
581 /*If this is a SHAM image, setup the copper list appropriately*/
585 setupSHAM(screen
,(UWORD
*)(sham
->sp_Data
));
588 /*If this is a MacroPaint image, setup the copper list*/
591 specialModes
=MACROPAINT
;
592 setupDynHires(screen
,(UWORD
*)(ctbl
->sp_Data
));
596 /* Otherwise, this is a normal ILBM*/
597 specialModes
=NORMAL_MODE
;
599 /*Bring the screen to the front*/
600 ScreenToFront(screen
);
602 /*If the user used the 'print' flag on the command line, print*/
603 /*the picture (but not if this is a SHAM or MacroPaint image)*/
604 if(printPics
&& specialModes
== NORMAL_MODE
)
605 dumpRastPort(&(screen
->RastPort
),&(screen
->ViewPort
));
609 /*Close the previous window and screen*/
611 CloseWindow(prevWindow
);
613 CloseScreen(prevScreen
);
615 /*Free the buffer that holds the BODY chunk information*/
616 FreeMem(bodyBuffer
,bufSize
);
618 /*Store the current window & screen structures, so they can be*/
628 if(ticks
==0) /*If ticks==0, this means that no delay was specified*/
629 { /*by the user. So just wait for him to click a button*/
631 int prevTopEdge
=prevScreen
->TopEdge
;
634 while((button
=checkButton())==none
&& rexxAbort
==none
)
636 /*Wait for 1/60th of a second*/
637 Wait (1L << prevWindow
->UserPort
->mp_SigBit
);
640 /*Refresh the SHAM copper list if required*/
641 if(prevTopEdge
!=prevScreen
->TopEdge
&& sham
!=NULL
)
643 prevTopEdge
=prevScreen
->TopEdge
;
644 setupSHAM(prevScreen
,(UWORD
*)(sham
->sp_Data
));
647 /*Refresh the MacroPaint copper list if required*/
648 if(prevTopEdge
!=prevScreen
->TopEdge
&& ctbl
!=NULL
)
650 prevTopEdge
=prevScreen
->TopEdge
;
651 setupDynHires(prevScreen
,(UWORD
*)(ctbl
->sp_Data
));
655 /*If its time to cycle the colors, then cycle them*/
656 if(cycle
&& numCycleColors
!=0 && --countDown
==0)
658 cycleColors(cycleTable
,destMap
,numCycleColors
,numColors
);
667 /*Check to see if the user wants to abort*/
668 if(button
==menu
|| rexxAbort
==menu
)
671 else /*Otherwise, wait for the specified amount of time*/
675 /*Wait 1/60th of a second*/
678 /*Cycle colors if necessary*/
679 if(cycle
&& numCycleColors
!=0 && --countDown
==0)
681 cycleColors(cycleTable
,destMap
,numCycleColors
,numColors
);
686 dispRexxPort(); /*Check ARexx port*/
689 button
=checkButton(); /*After each 25 ticks, check to see if*/
690 if(button
==menu
|| rexxAbort
==menu
) /*the user wants to abort*/
695 if(button
==select
|| rexxAbort
==select
) /*Or advance prematurely*/