PROGRAM IFF; { Lade - und Anzeigeprogramm für ILBM-Bilder Jens "Himpelsoft" Gelhar 1989/90 Dieses Programm kennen Sie vielleicht schon als Demo der frühen Versionen von KICK-Pascal. Es ist aber inzwischen stark verbessert worden. Zum einen konnte die Ladegeschwindigkeit auf ein recht gutes Niveau gesteigert werden, so daß man das lauffähige Programm wirklich sinnvoll benutzen kann. Außerdem kann man es jetzt auch direkt von der Workbench starten, indem man es entweder als "Default Tool" im Icon eines Bildes einträgt oder zuerst das Bild-Icon und dann mit "geSHIFTetem" Doppelklick ein Exe-File von "IFF.p" startet. Beim Start aus dem CLI kann man jetzt den Dateinamen als Parameter angeben, etwa Iff Bild Es gibt eine ganze Menge Optionen, die beim Start aus dem CLI gewählt werden können, indem man ihre Kennbuchstaben mit einem vorangestellten "-" in beliebiger Reihenfolge vor oder hinter dem Dateinamen angibt, etwa: IFF Bild -n -t Die Optionen kann man auch beim Workbench-Start benutzen, indem man im Icon des Bildes als "Tool Type" ein bestimmtes Wort schreibt. Hier ist eine Liste aller Optionen: -n Das Bild wird auf keinen Fall im Interlace-Modus dargestellt (ist besser für die Augen). Als Tool Type ist "NoInter" oder "NoLace" zu wählen. -i Das Bild WIRD im Interlace-Modus ausgegeben. Tool Type: "INTER" oder "LACE". -h erzwingt Hold-And-Modify-Modus. Tool Type: "HAM". Diese Option ist weitgehend überflüssig, denn bei Lo-Res-Bildern mit 6 Planes wird defaultmäßig der HAM-Mode gewählt. -l Option für Lo-Res-Modus (320*256 Punkte). Tool Type: "LoRes". -m schaltet auf hohe Auflösung (640 Punkte breit). Tool Type: "HiRes" oder "MedRes". -x wählt den Extra-Halfbrite-Modus, was aber nur bei Lo-Res-Bildern mit 6 Bitplanes geht. Beim Start von der Workbench ist "XHALF" als Tool Type anzugeben. -t "Talk"-Option: Während des Ladens werden Informationen über das Bild und die Bilddaten ausgegeben. Diese Option ist nur beim CLI-Start anwählbar. Das Bild wird so lange angezeigt, bis eine der Tasten Space, Return, Enter oder Escape gedrückt wird. Wenn das Bild größer als die Bildschirmauflösung ist, z. B. weil ein Hi-Res-Interlace-Bild mit der Option "-l" geladen wurde, kann der angezeigte Bildausschnitt mit den Cursortasten gescrollt werden. Die Bilddaten werden in der vorliegenden Version nicht mehr direkt auf den Bildschirm geladen, sondern zuerst in einen Puffer abgelegt, von wo jeweils der gerade aktuelle Ausschnitt (bei übergroßen Bildern) mit Zuhilfenahme des Blitters auf den Screen kopiert wird. Dieses Programm ist nicht nur ungemein nützlich - welcher Amiganer hat nicht im Laufe der Zeit eine mehr oder weniger umfangreiche IFF-Bildersammlung angelegt und will sie des öfteren ansehen, ohne erst ein Malprogramm o. Ä. zu laden - sondern auch lehrreich: es demonstriert folgende immer wiederkehrende Programmieraufgaben: - Parameterübernahme vom CLI und der Workbench. Wie Sie sehen können, ist es überhaupt nicht schwierig, einen Dateinamen von der Workbench zu holen. Man muß nur die Include-Datei "workbench/startup.h" einlesen und ein wenig mit Zeigern und Records hantieren. - Schnelle Dateihandhabung: Die alte Version von "IFF.p" war allein deshalb so lahm, weil die Daten in kleinen Portionen - Byte für Byte, teilweise auch Langwortweise - aus der Bilddatei gelesen wurden. Dabei macht sich dann bemerkbar, daß das DOS des Amiga nicht gerade schnell ist. In der neuen Version legt "IFF.p" einen 10 KByte großen Pufferspeicher an, aus dem dann byteweise gelesen werden kann, bis er leer ist und neu geladen werden muß. - Benutzung des Blitters. Aus einem Pufferspeicher, in den die Bilddaten zunächst geladen werden, werden sie mit dem Blitter blit(t)zschnell in die Bitmap des Screens kopiert. } { MaxonPascal3-Anpassung: Falk Zühlsdorff (PackMAN) 1994 benutzt: geänderte 'Workbench/startup.h' © by PackMAN bei Tooltype z.B. HAM=TRUE im Icon angeben... } {$opt q} USES Graphics,Intuition,Custom,DOS; {$incl "workbench/startup.h","icon.lib",'workbench/Workbench.h'} CONST BufSize = 10000; { Länge des Datei-Buffers } ScreenBar = 11; { Höhe Screen-Titelbalken } ScreenW = 336; { Breite Lo-Res-Screen } ScreenH = 250; { Höhe Lo-Res-Screen ohne Titelbalken} MaxPlane = 11; { höchstens 12 Planes } TYPE BitMapHeader = RECORD Width, Height: Word; dX, dY: Integer; Depth, Mask: Byte; Kompr, Pad: Boolean; transcolor: Word; XAspect, YAspect: Byte; SWidth, SHeight: integer END; VAR Fil: FILE OF BYTE; { IFF-Datei } FName: STRING; { Dateiname } FilLen, FilPos: Long; { Merker für "FileSize" und "FilePos" } ScrMode: Word; SBreite, SHoehe, Tiefe: integer; { Maße des geöffneten Screens } RGB: ARRAY [ 0..63 ] OF RECORD R,G,B: Byte END; Ende, FileEnde, ErrorFlag:Boolean; Talky, Inter, NonInter, HamMode, XHalf, LoRes, Res640: Boolean; HeadFlag, BodyFlag, CamgFlag, CmapFlag: Boolean; { Flags: Hunk gelesen ? } BMHD: BitMapHeader; { Inhalt des BMHD-Hunks } BitMaps: ARRAY[0..11] OF Long; BytesProZeile: integer; XOffs, YOffs: integer; { Scroll-Position } XStep, YStep: integer; { Scrool-Schrittweite } MyWindow: p_Window; MyScreen: p_Screen; Msg: p_IntuiMessage; { RAWKEY-Message } PROCEDURE ParameterAuswerten; { Bei Start von Workbench oder CLI Dateinamen des Bildes ermitteln } VAR Para: STRING; i: integer; PROCEDURE OverRead; BEGIN WHILE (Para[i] > chr(0) ) AND (Para[i] <= ' ') DO i:=i+1; IF (Para[i]='-') AND (Upcase( Para[ i+1 ] ) IN ['A'..'Z'] ) THEN BEGIN CASE Upcase( Para[ i+1] ) OF 'H': HamMode := true; 'I': Inter := true; 'L': LoRes := true; 'M': Res640 := true; 'N': NonInter := true; 'T': Talky := true; 'X': XHalf := true; OTHERWISE i := i-2 END; i := i + 2; OverRead END; END; PROCEDURE StartVonWorkbench; {einige Änderungen © by PackMAN} TYPE mytooltype = ^char; VAR StMess : p_WBStartup; DiskObj : p_DiskObject; OldLock : BPTR; i, j : integer; St : STRING[10]; toolarray : ^mytooltype; BEGIN OpenLib(IconBase,'icon.library',0); StMess := StartupMessage; IF StMess^.sm_NumArgs < 2 THEN FName := '' ELSE WITH StMess^.sm_ArgList^[2] DO BEGIN { Als Datei wird das Argument Nr. #2 genommen. Falls noch mehr Icons aktiviert sing (z. B. durch "Shift-Klick", werden diese ignoroert. } FName := wa_Name; { reiner Name ohne Pfad! Deshalb muss das aktuelle Verzeichnis entsprechend gewählt werden: } OldLock := CurrentDir( wa_Lock ); { "ToolTypes" holen } DiskObj := GetDiskObject( FName ); { Icon laden } IF DiskObj<>NIL THEN BEGIN toolarray:=DiskObj^.do_ToolTypes; St:=FindToolType(toolarray,'HAM'); IF St='true' THEN HamMode:=true; St:=FindToolType(toolarray,'NOLACE'); IF St='true' THEN NonInter:=true; St:=FindToolType(toolarray,'NOINTER'); IF St='true' THEN NonInter:=true; St:=FindToolType(toolarray,'LACE'); IF St='true' THEN Inter:=true; St:=FindToolType(toolarray,'INTER'); IF St='true' THEN Inter:=true; St:=FindToolType(toolarray,'HIRES'); IF St='true' THEN Res640:=true; St:=FindToolType(toolarray,'MEDRES'); IF St='true' THEN res640:=true; St:=FindToolType(toolarray,'LORES'); IF St='true' THEN LoRes:=true; FreeDiskObject( DiskObj ); END; END; CloseLib(IconBase); END; BEGIN Talky := false; { Defaultwerte für } Inter := false; { Optionen } NonInter := false; HamMode := false; LoRes := false; Res640 := false; XHalf := false; IF FromWB THEN StartVonWorkbench ELSE { Start vom CLI } BEGIN FName := ''; IF ParameterLen < 80 THEN BEGIN Para := ParameterStr; Para[ ParameterLen+1 ] := chr(0); i := 1; OverRead; IF Para[i] IN [chr(0),'?'] THEN BEGIN { Kein Parameter angegeben } writeln(#27'[33mIFF'#27'[31m - Laden und Anzeigen von IFF-Bildern'); writeln('Geschrieben von '#27'[33mJens Gelhar'#27'[31m 1990 mit '#27'[33mKICK-Pascal'#27'[31m.'); write (' [-h] [-i] [-l] [-m] [-n] [-t] [-x] : ' ); readln( Para ); i := 1; OverRead END; WHILE Para[i] > ' ' DO BEGIN FName := FName+Para[i]; i := i+1 END; OverRead; IF Para[i] <> chr(0) THEN Error( 'Falscher Parameter!' ) END END; END; PROCEDURE DateiOeffnen; BEGIN Reset ( Fil, FName ); IF IOResult<>0 THEN Error('Datei konnte nicht geöffnet werden.'); Buffer ( Fil, BufSize ); FilLen := FileSize( Fil ); FilPos := 0; ErrorFlag := false; FileEnde := false; END; PROCEDURE CloseAll; { Datei, Screen usw. Schließen } VAR i: integer; BEGIN Close( Fil ); FOR i:=0 TO MaxPlane DO IF BitMaps[i]<>0 THEN Free_Mem( BitMaps[i], BytesProZeile*BMHD.Height ); IF MyWindow<>NIL THEN Close_Window( MyWindow ); IF MyScreen<>NIL THEN Close_Screen( MyScreen ); IF NOT FromWB THEN Writeln; END; PROCEDURE LoadILBM; VAR Hunkname: STRING[5]; LongWord: Long; i, Zeile, Plane, Count: integer; IntText: IntuiText; CamgWord: Word; FormLen: Long; HunkEnd: Long; PROCEDURE FileError; BEGIN IF NOT ErrorFlag THEN BEGIN Writeln('File Error!'); ErrorFlag:=true; FileEnde := true END END; PROCEDURE Lies ( p: Ptr; L: Long); { "L" Bytes nach Adresse "p" laden } VAR p2: ^ARRAY[1..MaxLong] OF BYTE; i : Long; BEGIN p2 := p; IF ErrorFlag THEN { Fehler bereits aufgetreten => alles mit Nullen füllen } BEGIN FOR i:=1 TO L DO p2^[i]:=0; END ELSE BEGIN BlockRead(Fil, p2^, L); FileEnde := EoF(Fil); ErrorFlag := IoResult<>0 END; END; PROCEDURE OverRead (L:Long); { "L" Bytes überlesen } VAR buf:String[50]; BEGIN WHILE (L>50) and not ErrorFlag Do BEGIN Lies(^buf,50); L:=L-50; END; Lies(^Buf,L mod 50) END; PROCEDURE ReadHunkName; BEGIN Hunkname[5]:=chr(0); Lies(^Hunkname,4) END; PROCEDURE ReadLong; BEGIN Lies(^Longword,4); END; PROCEDURE LiesZeile(Adr:Long); Var Size: integer; i,j: integer; Head: Short; Body: Byte; Zeile: ^ARRAY[0..MaxInt] OF Byte; BEGIN IF Not ErrorFlag THEN BEGIN Zeile := Ptr(Adr); Size := ((BMHD.Width + 15) SHR 3) AND $FFE; IF not BMHD.Kompr THEN Lies(Ptr(Adr),Size) ELSE BEGIN i:=0; WHILE (i < Size) and not ErrorFlag Do BEGIN Lies( ^Head, 1 ); IF Head >= 0 THEN BEGIN Lies(Ptr(Adr+i),Head+1); i:=i+Head+1 END ELSE IF Head <> -128 THEN BEGIN Lies( ^Body, 1 ); FOR j := i TO i-Head DO Zeile^[j] := Body; i := 1 + i - Head; END END END; END END; PROCEDURE Hunk_BMHD; BEGIN IF HeadFlag THEN FileError; Lies( ^BMHD, SizeOf(BitMapHeader) ); OverRead( LongWord-SizeOf(BitMapHeader) ); WITH BMHD DO BEGIN IF Talky THEN BEGIN Writeln('Breite: ',Width); Writeln('Höhe: ',Height); Writeln('Tiefe: ',Depth); Writeln('Screen: ',SWidth,'*',SHeight); Writeln('Maske: ',Mask); IF Kompr THEN Writeln('Komprimiert') END; SBreite := ScreenW; SHoehe := ScreenH; ScrMode:=GENLOCK_VIDEO; Tiefe := Depth; { Grafikmodus und Auflösung ermitteln: } IF SHeight > ScreenH THEN { Interlace erforderlich } BEGIN ScrMode := ScrMode+LACE; SHoehe := 2*SHoehe END; IF SWidth>ScreenW THEN BEGIN ScrMode := ScrMode+HIRES; SBreite := 2*SBreite END; IF (SBreite=ScreenW) AND (Tiefe=6) THEN ScrMode := ScrMode+HAM; { Bei 6 Planes Lo-Res wird HAM vermutet. } BytesProZeile := ((Width+15) AND $fff0) SHR 3; FOR i:=0 TO Depth-1 DO Bitmaps[i] := Alloc_Mem( BytesProZeile*Height, 2); END; HeadFlag := true; END; PROCEDURE Hunk_CMAP; VAR i: integer; BEGIN Cmapflag := true; FOR i:=0 TO LongWord DIV 3-1 DO Lies( ^RGB[i AND $3f], 3 ); END; PROCEDURE Hunk_BODY; BEGIN IF Bodyflag OR NOT HeadFlag THEN FileError; FOR Zeile := 0 TO BMHD.Height-1 DO FOR Plane := 0 TO BMHD.Depth-1 DO LiesZeile( Bitmaps[Plane] + Zeile*BytesProZeile ); BodyFlag := true END; PROCEDURE Hunk_CAMG; VAR i: integer; PROCEDURE Hexx(n: Word); BEGIN IF n>15 THEN Hexx(n DIV 16); write('0123456789abcdef'.[n AND $f +1]); END; BEGIN CamgFlag := true; FOR i:=1 TO (LongWord+1) DIV 2 DO BEGIN Lies(^CamgWord,2); END; IF Talky THEN BEGIN write(' Viewmode: '); Hexx(CamgWord); writeln; END; END; BEGIN { Proc LoadILBM } HeadFlag := false; Bodyflag := false; CamgFlag := false; CmapFlag := false; FOR i:=0 TO MaxPlane DO Bitmaps[i] := 0; ReadHunkName; IF HunkName<>'FORM' THEN BEGIN CloseAll; Error('Kein IFF-Format.') END; IF NOT FromWB THEN Write('Loading ',FName,'...'); IF Talky THEN Writeln; ReadLong; FormLen := LongWord; ReadHunkName; IF HunkName<>'ILBM' THEN BEGIN CloseAll; Error('Kein ILBM-File.') END; WHILE FilePos(Fil)+12 < FileSize(Fil) DO BEGIN ReadHunkName; ReadLong; IF Talky THEN Writeln( HunkName, LongWord:8, ' Bytes' ); { Nur Hunks gerader Länge: } LongWord := (LongWord + 1) AND $FFFFFe; HunkEnd := FilePos(Fil) + LongWord; IF HunkName='BMHD' THEN Hunk_BMHD ELSE IF HunkName='CMAP' THEN Hunk_CMAP ELSE IF HunkName='BODY' THEN Hunk_BODY ELSE IF HunkName='CAMG' THEN Hunk_CAMG; IF ( FilePos(Fil) < HunkEnd ) AND ( HunkEnd < FileSize(Fil) ) THEN OverRead( HunkEnd-FilePos(Fil) ); END; IF NOT HeadFlag THEN Exit; { Grafikmodus, Auflösung und Bildschirmgröße ermitteln } IF CamgFlag THEN ScrMode:=CamgWord; IF HAMMode AND (Tiefe=6) THEN BEGIN SBreite := ScreenW; ScrMode := ScrMode OR HAM END; IF LoRes THEN BEGIN SBreite := ScreenW; SHoehe := ScreenH; ScrMode := Scrmode AND NOT(HIRES OR LACE) END; IF Res640 THEN BEGIN SBreite := 2*ScreenW; ScrMode := ScrMode OR HIRES; END; IF Inter THEN BEGIN SHoehe := 2*ScreenH; ScrMode := ScrMode OR LACE END; IF NonInter THEN BEGIN SHoehe := ScreenH; ScrMode := ScrMode AND NOT LACE END; IF XHalf AND (Tiefe=6) THEN BEGIN SBreite := ScreenW; ScrMode := ScrMode OR EXTRA_HALFBRITE AND NOT HAM END; IF SHoehe > BMHD.Height THEN SHoehe := BMHD.Height; IF SBreite > BMHD.Width THEN SBreite := BMHD.Width; XOffs := (BMHD.Width-SBreite) DIV 2; YOffs := (BMHD.Height-SHoehe) DIV 2; XStep := (BMHD.Width DIV 80 + 15) AND $7ff0; YStep := BMHD.Height DIV 40; END; PROCEDURE OeffneScreen; { Nach Ergebnissen von LoadILBM Bildschirm öffnen } VAR i : integer; MyView : Ptr; BEGIN MyScreen:=Open_Screen( 0, 0, SBreite, SHoehe+ScreenBar, Tiefe, 0, 1, ScrMode, FName); MyWindow:=Open_Window( 0, ScreenBar, SBreite, SHoehe, 1, RAWKEY,ACTIVATE+BORDERLESS, Nil, MyScreen, SBreite, SHoehe, SBreite, SHoehe ); MyView := ^MyScreen^.ViewPort; FOR i:=0 TO 1 SHL Tiefe -1 DO WITH RGB[i] DO SetRGB4( MyView, i, r div 16, g div 16, b div 16) END; PROCEDURE DisplayPicture; VAR Plane: integer; Wort: Word; PROCEDURE BlitIt (Von, Nach: Long; x,y,Mod1,Mod2: integer); { Anfangsadressen "Von", "Nach", "x" Worte breit, "y" Zeilen, Modulowerte in Bytes (!) } VAR i, j, k: integer; v, n: ^ARRAY[0..MaxInt] OF Word; C: Custom ABSOLUTE $dff000; BEGIN OwnBlitter; { Zitat A. Krämer: "OwnBlitter! Hör' mir bloß auf mit Ownblitter!" Eigentlich sollte jene Funktion warten, bis der Blitter mit seiner vorherigen Operation fertig ist. Macht sie aber nicht immer. Deshalb sorgen wir selbst dafür, daß mit dem Erteilen des nächsten Auftrags gewartet wird: } WHILE C.DMACONR AND $4000 <> 0 DO; C.BLTAPT := Ptr(Von); C.BLTDPT := Ptr(Nach); C.BLTAMOD := Mod1; C.BLTDMOD := Mod2; C.BLTCON0 := %0000100111110000; C.BLTCON1 := 0; C.BLTAFWM := $ffff; C.BLTALWM := $ffff; C.BLTSIZE := (y AND $3FF) SHL 6 + (x AND $3F); DisownBlitter END; BEGIN Wort := (SBreite+15) shr 4; WITH MyScreen^.Bitmap DO FOR Plane:=0 TO BMHD.Depth-1 DO BlitIt ( BitMaps[Plane] + 2*(XOffs SHR 4) + YOffs*BytesProZeile, Long( Planes[Plane] ) + ScreenBar*BytesPerRow, Wort, SHoehe, BytesProZeile- 2 * Wort, (BytesPerRow+1)AND $FFE - 2*Wort ); END; BEGIN MyScreen := NIL; MyWindow := NIL; ParameterAuswerten; IF FName='' THEN Exit; DateiOeffnen; LoadILBM; IF NOT (HeadFlag AND Bodyflag) THEN Exit; { Kein Bild geladen!! } OeffneScreen; DisplayPicture; Ende := false; REPEAT Msg := Wait_Port(MyWindow^.UserPort); Msg := Get_Msg(MyWindow^.UserPort); CASE Msg^.Code OF $4c: BEGIN IF YOffs >= YStep THEN YOffs := YOffs-YStep ELSE YOffs := 0; DisplayPicture END; $4d: BEGIN IF YOffs+YStep+SHoehe <= BMHD.Height THEN YOffs := YOffs+YStep ELSE YOffs := BMHD.Height-SHoehe; DisplayPicture END; $4e: BEGIN IF XOffs+XStep+SBreite <= BMHD.Width THEN XOffs := XOffs+XStep ELSE XOffs := BMHD.Width-SBreite; DisplayPicture END; $4f: BEGIN IF XOffs >= XStep THEN XOffs := XOffs-XStep ELSE XOffs := 0; DisplayPicture END; $40, $43, $44, $45: Ende := true; OTHERWISE { andere Taste } END; REPEAT { Nachlaufen vermeiden! } Msg := Get_Msg(MyWindow^.UserPort) UNTIL Msg=NIL; UNTIL Ende; CloseAll END.