Files
erkyrath.infocom-zcode-terps/mac/gfx.p
Andrew Plotkin b642da811e Initial commit.
2023-11-16 18:19:54 -05:00

1630 lines
44 KiB
OpenEdge ABL
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{=======================================}
{ GRAPHICS ROUTINES }
{=======================================}
{ these routines work with compressed, multi-picture gfx files }
CONST
MAXGFXCOLORS = 16;
GHEADLEN = 16; { length of gfx megafile header }
{ header flag bits }
GF_EHUFF = $02; { dir entries include Huff ptrs (may be zero) }
GF_GHUFF = $04; { global Huff ptr is included in file header }
{ define some "custom" flag bits (high so won't clash) }
GF_SPLASH = $80; { startup screen, displayed while game loads }
{ entry flag bits }
GF_TRANS = $0001; { this pic uses Color 0 for "transparent" }
GF_PHUFF = $0002; { this pic is huffed }
GF_XOR = $0004; { this pic was XORed every second line }
GF_MONO = $0008; { this pic is mono, and scaled for a 480x300 screen (std Mac) }
{ PICSET stuff }
{ Note: could instead get picset buflen from gfx_Header (adjusted for each game),
OR could set it arbitrarily large, based on available memory. }
PSDEFLEN = 5*1024; { default buflen }
MAXITEMLEN = 1024; { "prevent loading of inappropriate (too big) pics" }
PSENTRYLEN = 6; { 3 words (for now), id/off/len }
PSINITMARK = 987; { magic number }
TYPE
WPtr = ^INTEGER;
LPtr = ^LONGINT;
gfx_Header = { file's master header }
RECORD
gflags: INTEGER; { file id (byte), global flags (byte) }
ghuffOff:
LONGINT; { global huff ptr [optional] }
npics, { number of local directory entries }
nxpics: INTEGER; { [Mac: unused] }
entryLen, { length of each entry (12-14-16) }
cksum,
version: { current version of gfx file format }
INTEGER;
{ additional fields }
inited: BOOLEAN; { true after successful init }
dirLen: LONGINT; { length of entire directory }
LBYTES: INTEGER; { #bytes to specify data len (starting at each dataOff) }
END;
gfx_Entry = { one directory entry (in gfx megafile) }
RECORD
id: INTEGER;
rx,
ry: INTEGER; { raw size (from disk) }
eflags: INTEGER; { flags for this entry [vs. header flags] }
dataOff,
palOff,
huffOff:
LONGINT;
{ additional fields }
trans, { [copy of GF_TRANS flag] }
mono, { [copy of GF_MONO flag] }
{ fcolor, }
fstip: BOOLEAN; { set if stippling active }
x,
y: INTEGER; { internal size (after shrinking, stips) }
depth: INTEGER; { 4 (or 1 if stippled) }
rowBytes: INTEGER;
dx,
dy: INTEGER; { display size; may be scaled (1.5, 2.0) }
END;
RGB_24 = PACKED ARRAY[1..3] of char; { 24 bits }
gfx_Palette = { (potentially) shared color/stipple tables }
RECORD
pOff: LONGINT;
pSiz: INTEGER;
palette:
PACKED ARRAY[1..3*MAXGFXCOLORS] of char;
{ PACKED ARRAY[1..MAXGFXCOLORS] of RGB_24; }
sSiz: INTEGER;
stips: PACKED ARRAY[1..MAXGFXCOLORS] of char; { stipple id table }
{ extended stipple table (shifted & packed Values - 4 sets of Words) }
exstips:
PACKED ARRAY[1..MAXGFXCOLORS*2*4] of char;
END;
gfx_Hufftree = { (potentially) shared huffman data }
RECORD
hOff: LONGINT;
hufftree:
PACKED ARRAY[1..256{max}] of char;
END;
gfx_Picset = { picture buffering vars }
RECORD
psInited: INTEGER; { marked after successful init }
psPtr: Ptr; { overall ptr; id stuff starts here }
psLen: LONGINT; { overall (allocated) len }
psData: Ptr; { data starts here [recalced for each picset] }
p: Ptr; { search results returned here }
len: LONGINT;
END;
gfx_Data =
RECORD { globals for data buffer }
pMin: Ptr;
minSize, { fully compressed size }
midSize, { size AFTER dehuff, but BEFORE derun }
maxSize: LONGINT { fully decompressed size (expected) }
END;
uncompRec =
RECORD { this block passed to UncompH }
inbuf,
outbuf,
huff_tree: Ptr;
pic_x, { pixel width }
midlen, { after unhuff, before de-run }
outbuflen: LONGINT; { "interrupt count" }
firstCall, { raised by caller, initially }
lastCall: Boolean; { raised by callee, when done }
saveRegs: ARRAY[1..16] OF LONGINT; { 16x4 bytes max }
END;
uncompRecPtr = ^uncompRec;
VAR
gfxChan: INTEGER; { gfx megafile }
dirPtr: Ptr; { gfx directory, in core }
gh: gfx_Header;
ge: gfx_Entry;
gp: gfx_Palette;
gt: gfx_Hufftree;
ps: gfx_Picset;
gd: gfx_Data;
{-------------------------------}
{ PutWord, PutLong }
{-------------------------------}
{ Following are some useful, general-purpose routines }
PROCEDURE PutWord (VAR p: Wptr; word: INTEGER); { store word, advance ptr }
BEGIN
p^ := word;
p := WPtr (ORD4(p) + 2);
END;
(**
PROCEDURE PutLong (VAR p: Lptr; long: LONGINT); { store long, advance ptr }
BEGIN
p^ := long;
p := LPtr (ORD4(p) + 4);
END;
**)
{-------------------------------}
{ GetWord, GetWordB, GetLong } { see also GTAWord }
{-------------------------------}
FUNCTION GetWord (VAR p: Wptr): INTEGER; { return word, advance ptr }
BEGIN
GetWord := p^;
p := WPtr (ORD4(p) + 2);
END;
{ use this call if the word might be odd-aligned }
(**
FUNCTION GetWordB (VAR p: Wptr): INTEGER; { return word, advance ptr }
BEGIN
GetWordB := BOR (BSL (Ptr(p)^, 8),
BAND (Ptr (ORD4(p)+1)^, 255));
p := WPtr (ORD4(p) + 2);
END;
FUNCTION GetLong (VAR p: Lptr): LONGINT; { return long, advance ptr }
BEGIN
GetLong := p^;
p := LPtr (ORD4(p) + 4);
END;
**)
{-------------------------------}
{ RoundUp, RoundDown }
{-------------------------------}
FUNCTION RoundUp (n: LONGINT): LONGINT;
BEGIN
IF Odd (n) THEN RoundUp := n+1 ELSE RoundUp := n;
END;
FUNCTION RoundDown (n: LONGINT): LONGINT;
BEGIN
IF Odd (n) THEN RoundDown := n-1 ELSE RoundDown := n;
END;
{-------------------------------}
{ LongMult } { "LongMul" causes a conflict }
{-------------------------------}
{ use this call if a product of two integers might exceed 32K,
to prevent bad Pascal sign-extension }
FUNCTION LongMult (a, b: INTEGER): LONGINT;
BEGIN
LongMult := LONGINT (a) * LONGINT (b);
END;
{-------------------------------}
{ FSReadGFX, FSReadClr }
{-------------------------------}
{ read from gfx file, starting from given offset (-1 means current) }
FUNCTION FSReadGFX (off, len: LONGINT; p: Ptr): osErr;
BEGIN
IF off <> -1 THEN
IF SetFPos (gfxChan, FSFROMSTART, off) <> noErr THEN
BEGIN
FSReadGFX := -1; { report an error }
EXIT (FSReadGFX);
END;
FSReadGFX := FSRead (gfxChan, len, p);
END;
{ a handy func to Read and right-justify a value, of len < n bytes,
into a n byte field. The unused bytes are zeroed. }
FUNCTION FSReadClr (off, len: LONGINT; p: Ptr; n: LONGINT): osErr;
BEGIN
n := n - len;
WHILE n > 0 DO { clear the high bytes }
BEGIN
p^ := 0;
p := Ptr (ORD4(p) + 1);
n := n - 1;
END;
FSReadClr := FSReadGFX (off, len, p); { then read as usual }
END;
{-------------------------------}
{ varCopy, varCopyClr }
{-------------------------------}
PROCEDURE varCopy (p1, p2: Ptr; n: INTEGER);
BEGIN
WHILE n > 0 DO
BEGIN
p2^ := p1^;
p1 := Ptr (ORD4(p1) + 1);
p2 := Ptr (ORD4(p2) + 1);
n := n - 1;
END;
END;
{ a handy func to Copy and right-justify a value, of len < ntot bytes,
into a ntot-byte field. The unused bytes are zeroed. }
PROCEDURE varCopyClr (p1, p2: Ptr; n, ntot: INTEGER);
BEGIN
ntot := ntot - n;
WHILE ntot > 0 DO { clear the high bytes in dest }
BEGIN
p2^ := 0;
p2 := Ptr (ORD4(p2) + 1);
ntot := ntot - 1;
END;
varCopy (p1, p2, n); { then copy as usual }
END;
{-------------------------------}
{ ReadGFXEntry }
{-------------------------------}
FUNCTION binary_search (dir: Ptr; e_count, e_size, id: LONGINT): Ptr;
C; EXTERNAL;
{ read [into master record] a local directory entry }
FUNCTION ReadGFXEntry (id: INTEGER): OsErr;
VAR
pEnt, pRec: Ptr;
curDepth: INTEGER;
PROCEDURE doCopy (n: INTEGER);
BEGIN
WHILE n > 0 DO
BEGIN
pRec^ := pEnt^;
pEnt := Ptr (ORD4(pEnt) + 1);
pRec := Ptr (ORD4(pRec) + 1);
n := n - 1;
END;
END;
PROCEDURE doZero (n: INTEGER);
BEGIN
WHILE n > 0 DO
BEGIN
pRec^ := 0;
pRec := Ptr (ORD4(pRec) + 1);
n := n - 1;
END;
END;
BEGIN
ReadGFXEntry := -1; { default }
IF NOT gh.inited
THEN EXIT (ReadGFXEntry); { make sure file opened okay }
IF ge.id = id THEN { same id as last time? }
BEGIN
ReadGFXEntry := 0; { yes, all done }
EXIT (ReadGFXEntry);
END;
pEnt := binary_search (dirPtr, gh.npics, gh.entryLen DIV 2, id);
IF pEnt = NIL
THEN EXIT (ReadGFXEntry);
pRec := @ge;
doCopy (8); { read first 4 fields }
ge.trans := BAND (ge.eflags, GF_TRANS) <> 0;
ge.mono := BAND (ge.eflags, GF_MONO) <> 0;
doZero (1);
doCopy (3); { read/align dataOff }
{ is there a palette entry? }
IF ge.mono THEN { NO [implied] }
BEGIN
doZero (4);
pEnt := Ptr (ORD4(pEnt) + 1); { skip over /single/ pad byte }
END
ELSE { Yes, read/align palOff }
BEGIN
doZero (1);
doCopy (3);
END;
IF BAND (gh.gflags, GF_EHUFF + GF_GHUFF) = GF_EHUFF {E on, G off?} THEN
BEGIN
doZero (2);
doCopy (2); { read/align local huff ptr }
ge.huffOff := 2 * ge.huffOff; { [make byte offset] }
END
ELSE
ge.huffOff := gh.ghuffOff; { else default to global ptr }
ge.y := ge.ry; { initially, set to AM values }
ge.x := ge.rx;
ge.depth := 4;
IF ge.mono THEN { special pic (mono, sized) for std Mac? }
ge.fstip := FALSE
ELSE
BEGIN
IF myCW { get current depth }
THEN curDepth := CGrafPtr(myWindow)^.portPixMap^^.pixelSize
ELSE curDepth := 1;
{ detect if stippling needed }
ge.fstip := (curDepth = 1);
END;
{ must be a multiple of /4/ for in-place stippling (divides it by 2) }
ge.rowbytes := CalcRow4Bytes (ge.rx, GFXAM_DEPTH);
{ set dy/dx now, so PICINF will report correctly }
IF ge.mono OR myTiny THEN { scale 1x for display }
BEGIN
ge.dy := ge.ry;
ge.dx := ge.rx;
END
ELSE IF myBig THEN { scale 2x for display }
BEGIN
ge.dy := ge.ry * 2;
ge.dx := ge.rx * 2;
END
ELSE { scale 1.5x for display }
BEGIN
ge.dy := (ge.ry * 3) DIV 2;
ge.dx := (ge.rx * 3) DIV 2;
IF ge.fstip THEN { must be even, round down }
BEGIN
ge.dy := RoundDown (ge.dy);
ge.dx := RoundDown (ge.dx);
END;
END;
ReadGFXEntry := 0; { all okay }
END;
{-------------------------------}
{ ReadGFXPalette }
{-------------------------------}
PROCEDURE BuildStips (table1, table2: Ptr);
EXTERNAL; {68K}
PROCEDURE FillCSpec (CSpec: Ptr; ix: INTEGER; bytePal: Ptr);
EXTERNAL; {68K}
{ read and setup palette (color or stipple); also read huffman tree }
FUNCTION ReadGFXPalette: OSErr;
VAR
n: INTEGER;
p: Ptr;
myPal: PaletteHandle;
{ a local procedure }
PROCEDURE doRead (off, n: LONGINT; p: Ptr);
BEGIN
IF FSReadGFX (off, n, p) <> noErr THEN EXIT (ReadGFXPalette);
END;
BEGIN
ReadGFXPalette := -1; { default val }
IF (ge.palOff <> 0) AND (gp.pOff <> ge.palOff) THEN { get new palette }
BEGIN
gp.psiz := 0; { zero high byte }
p := Ptr (ORD4(@gp.psiz) + 1);
doRead (ge.palOff, 1, p); { read low byte }
IF gp.psiz > MAXGFXCOLORS
THEN EXIT (ReadGFXPalette);
doRead (-1, 3*gp.psiz, @gp.palette);
{ We ALWAYS read and process the stipple data. It doesn't take much time, and
it means we are always ready to switch into stipple mode if the user changes
the screen. Note: when all games have their own b/w files, we should avoid
this in b/w mode. }
gp.ssiz := 0; { zero high byte }
p := Ptr (ORD4(@gp.ssiz) + 1);
doRead (-1, 1, p); { read low byte }
IF gp.ssiz > MAXGFXCOLORS
THEN EXIT (ReadGFXPalette);
doRead (-1, gp.ssiz, @gp.stips);
{ use stipple palette to build extended/value table }
BuildStips (@gp.stips, @gp.exstips);
{ As of now, we process the color data ONLY if we're currently in color mode,
not stipple mode. Means that switching from stips to color in mid-game will
temporarily (until the next palette is read) display strange colors. }
IF NOT ge.fstip THEN { AND NOT ge.mono [implicit from paloff check] }
{ use zcolor palette to build custom color table }
BEGIN
WITH gfxPM.pmTable^^ DO { dereference outside of loop }
BEGIN
p := @gp.palette;
{ don't touch initial, final entries (set up during init)
Final color table layout is: 1, 2-15, 0 (see IM V-158) }
FOR n := 1 TO MAXGFXCOLORS-2 DO
BEGIN
{ first two args are 0-origin }
FillCSpec (@ctTable[n], n+1, p);
p := Ptr (ORD4(p) + 3);
(**) { install in altscreen too, for Blits }
{ Otherwise, updates map all colors to Black ... }
altPM^^.pmTable^^.ctTable[n] := ctTable[n];
(**)
END;
{ tell system that colors may have changed }
ctSeed := GetCTSeed;
(**) altPM^^.pmTable^^.ctSeed := GetCTSeed; (**)
{ if gp.psiz < 16, should it be copied into palette? }
END;
{ go whap on palette -- install new colors with zero tolerance }
myPal := GetPalette (myWindow); { get default [from GetNewCWindow] }
(**) SetPalette (myWindow, myPal, TRUE {FALSE}); (**) { DO get color updates }
CTab2Palette (gfxPM.pmTable, myPal, pmTolerant, $0000);
ActivatePalette (myWindow);
END;
gp.pOff := ge.palOff; { remember which palette this is }
END;
IF BAND (ge.eflags, GF_PHUFF) <> 0 THEN { is /this/ pic huffed? }
IF gt.hOff <> ge.huffOff THEN { tree not already loaded? }
BEGIN
doRead (ge.huffOff, 256 {max}, @gt.hufftree);
gt.hOff := ge.huffOff; { remember which tree this is }
END;
ReadGFXPalette := 0; { all okay }
END;
{-------------------------------}
{ ReadGFXData }
{-------------------------------}
FUNCTION searchPicset (id: INTEGER): osErr;
FORWARD;
FUNCTION UncompH (p: uncompRecPtr): LONGINT; EXTERNAL; { now in 68K, for speed }
(**
FUNCTION uncompress_huff (inbuf, outbuf, huff_tree: Ptr;
inlen, pic_x: LONGINT): LONGINT; C; EXTERNAL; { dead }
FUNCTION uncompress_nohuff (inbuf, outbuf: Ptr;
inlen, pic_x: LONGINT): LONGINT; C; EXTERNAL; **)
{ Read in (or find in PICSET) the data for one picture, then decompress it.
A single large buffer, allocated at startup and shared with BLITBITS, is used
for all operations. By avoiding dynamic allocations, we guarantee that
PICDISP will never fail due to an out-of-memory error. By sharing with BlitBits,
we minimize gfx memory appetite.
The size of the buffer is equal to the maximum size of a decompressed picture
(plus one extra scanline). (For a monochrome screen, the buffer also includes
room for a /simultaneous/ full screen blit, used for Transparent mode.)
The data (if not found in PICSET) is initially read into the high end
of the buffer, then decompressed into the low end. }
FUNCTION ReadGFXData: OSErr;
VAR
p: Ptr;
dummy: INTEGER;
max2: LONGINT; { fully decompressed size (actual) }
ucr: uncompRec;
BEGIN
ReadGFXData := -1; { default val }
IF searchPicset (ge.id) = 0 THEN { first, check cache }
BEGIN
gd.pMin := ps.p;
gd.minSize := ps.len;
END
ELSE
BEGIN
IF FSReadClr (ge.dataOff, gh.LBYTES, @gd.minSize, 4) <> noErr { read size bytes }
THEN EXIT (ReadGFXData);
IF gd.minSize > altLen { fits in buffer? }
THEN EXIT (ReadGFXData);
{ For YZIP, always call CheckMyUpdate before doing any output. The case of graphics output
is tricky:
- must call /before/ gfx data is read into altbuf, since that action trashes the screen
image saved in the shared buffer!
- must call /after/ any call that could trigger a disk-swap request. These include:
* ReadGFXEntry - "never" (directory always preloaded)
* ReadGFXPalette - "sometimes" (only if different than previous)
* 2-byte size word, above - "usually" (unless cached by OS)
- in the nasty worst case, none of the above triggers the request, but reading the data
itself does. Since caching is MRU (presumably?), this could happen only if the 'size word'
existed withing the same block as a more recent picture with a slightly lower id. COULD
test for this case by touching the next disk block with a special read:
dummy := FSReadGFX (ge.dataOff+512 (1K? 2K?), 2, @dummy);
but this would routinely thrash (hiccup) the disk for every picture (or only pics that
crossed tracks? How about effect on load speed?)
Bottom line - we don't check for this, and hope it happens rarely or never. The ultimate
solution would be to have a completely independant buffer for gfx (or none at all, and output
directly to screen -- difficult on Mac).
}
CheckMyUpdate;
{ make sure gd.pMin is word-aligned, for reading speed }
gd.pMin := Ptr (ORD4(altPtr) + (altLen - RoundUp(gd.minSize)));
{ read in the (compressed) data }
IF FSReadGFX (-1, gd.minSize, gd.pMin) <> noErr
THEN EXIT (ReadGFXData);
END; { of IF searchPicset ... }
{ decompress (into bytes). actual data starts at second row.
For now, we've stopped supporting the not-huffed case. If this becomes a problem,
need only to create an UNCOMPNH based on UNCOMPH. }
gd.maxSize := LONGINT (ge.rx) * LONGINT (ge.ry); { in bytes }
IF BAND (ge.eflags, GF_PHUFF) = 0 THEN
BEGIN
(** max2 := uncompress_nohuff (gd.pMin, altPtr, gd.minSize, ge.rx) **)
EXIT (ReadGFXData); { "not handled" }
END;
varCopyClr (gd.pMin, @gd.midSize, gh.LBYTES, 4);
gd.pMin := Ptr (ORD4(gd.pMin) + gh.LBYTES);
{ [for mono/stips, decompression and display processing are now combined, and handled elsewhere.] }
IF ge.mono OR ge.fstip THEN
BEGIN
ReadGFXData := 0; { [not an error] }
EXIT (ReadGFXData);
END;
IF gd.maxSize + ge.rx > altLen THEN { fits in buffer? }
BEGIN
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
EXIT (ReadGFXData); { avoid a crash }
END;
WITH ucr DO
BEGIN
inbuf := gd.pMin;
outbuf := altPtr;
huff_tree := @gt.hufftree;
pic_x := ge.rx;
midlen := gd.midSize {gd.minSize};
outbuflen := altlen; { no interrupt for color }
firstCall := TRUE;
lastCall := FALSE;
END;
max2 := UncompH (@ucr);
IF max2 <> gd.maxSize THEN { /should/ be exact pic size }
BEGIN
IF gfxDebug
THEN SysBeep (4);
{ EXIT (ReadGFXData); } { [don't abort for this one] }
END;
ReadGFXData := 0; { return with no errors }
END;
{-------------------------------}
{ CopyPic }
{-------------------------------}
{ copy to OR from the screen }
{ >> careful: pass Pixmaps by name not value, else chopped ! << }
{ (pass rects by name, only to avoid making copies) }
PROCEDURE CopyPic (srcBits, dstBits: BitMapPtr; VAR r1, r2: Rect);
VAR
mode: INTEGER;
BEGIN
mode := SRCCOPY; { default }
IF NOT (ge.fstip OR ge.mono) { i.e., if color }
THEN IF ge.trans { color 0 means "transparent" }
THEN mode := 36 {TRANSPARENT};
{ use transIndex (in ColorTable) instead ?? }
{ draw (a piece of) the bitmap to the screen }
ZCopyBits (srcBits, dstBits, r1, r2, mode, NIL);
END;
{-------------------------------}
{ ShowGFXM }
{-------------------------------}
CONST
DECOMP_LEN = 8*1024; { ~16 rows x 480 }
DECOMP_MINLEN = 4*1024;
TYPE
MonoPicRec =
RECORD { this block passed to Monopic }
srcX, { in pixels }
srcY,
dstRB: INTEGER; { in bytes }
trans: Boolean; { transparency flag }
psrc, { note: src rows are unpadded }
pdst: Ptr; { [return updated ptr] }
END;
MonoPicRecPtr = ^MonoPicRec;
PROCEDURE MonoPic (p: MonoPicRecPtr); EXTERNAL; {68K}
(** PROCEDURE MonoPic (srcX, srcY, dstRB: INTEGER; src, dst: Ptr); EXTERNAL; {68K} **)
{ Display, in straight Mono, (a section of) a picture, data in altbuf;
position given in pixel units.
Because a mono pic decompresses into so many bytes (144000 prior to MonoPic), we
now break up decompression and mono rendering into smaller chunks. To minimize overhead
lossage, chunks shouldn't be too small. Our buffer is partitioned as follows:
---------------------------------------
| Transbuf (18000 bytes) |
| |
---------------------------------------
| Outbuf (DECOMP_LEN or more) |
---------------------------------------
| Inbuf (18000 bytes or less) |
| |
--------------------------------------- }
PROCEDURE ShowGFXM (ypos, xpos: INTEGER);
VAR
r1, r2: rect;
pDecomp, { was pMax }
pTrans: Ptr;
max2, { fully decompressed size (actual) }
decompLen, { space available for decompression }
decompLen1, { ... rounded down to exact multiple of picx }
decompRet, { #bytes returned in latest decomp buffer }
transLen: LONGINT; { length of transparency buffer }
decompRows: INTEGER; { #rows returned per UncompH call }
unroll: Boolean;
ucr: uncompRec;
mpr: MonoPicRec;
(** debug_ticks: LONGINT; **) { FOR TIMING TESTS ONLY }
BEGIN
(** debug_ticks := TickCount; **)
ge.depth := 1; { adjust for mono }
ge.rowbytes := CalcRowBytes (ge.x, 1); { recalc this one }
{ calc rects (for "straight mono", always scaled 1.00) }
SetRect (r1, 0, 0, ge.x, ge.y);
r2 := r1;
(** SetRect (r2, 0, 0, ge.dx, ge.dy); **) { same }
OffsetRect (r2, wMarg + xpos, ypos);
(** rawlen {gd.maxSize} := LongMult (ge.x, ge.y); **) { DON'T CARE, use chunks now }
transLen := LongMult (ge.y, ge.rowbytes);
decompLen := altLen - (transLen + gd.minSize);
IF decompLen < DECOMP_MINLEN THEN { minSize was too big! }
BEGIN
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
EXIT (ShowGFXM);
END;
{ allow one extra line for unEOR, plus 128 bytes max for unRun spillover }
decompRows := (decompLen - (ge.x + 128)) DIV ge.x;
IF decompRows > ge.y THEN { don't exceed actual pic height }
decompRows := ge.y;
{ further strategy: if it's a "big" pic, then limit decompRows to a small number and
incrementally "unroll" the actual display, to avoid a long dead moment. The smaller
decompRows is, the smoother the unroll but the bigger the overhead.
Experiments with "debug_ticks" (on a Mac 512K) show that dividing a 480x300 pic into
n-row chunks increases the total display time as follows:
n=300 0
n=4 0.23 sec
n=2 0.45 sec
n=1 0.90 sec (ouch)
}
IF translen < (18000 DIV 4) THEN { is pic smaller than 1/4 screen? }
unroll := FALSE { don't bother to unroll }
ELSE
BEGIN
IF decompRows > 2 {4} THEN { n=2 looks quite smooth }
decompRows := 2 {4};
unroll := TRUE;
END;
decompLen1 := LongMult (decompRows, ge.x);
pTrans := altPtr;
pDecomp := Ptr (ORD4(altPtr) + transLen);
WITH gfxPM DO
BEGIN
baseAddr := pTrans; { set gfxPM base to Trans base }
rowBytes := ge.rowBytes;
bounds := r1;
END;
{ if Transparent, blit in (entire) old pic first }
IF ge.trans THEN
CopyPic (@thePort^.portBits, BitMapPtr(@gfxPM), r2, r1);
IF unroll THEN
BEGIN
r1.bottom := r1.top; { setup for unroll }
r2.bottom := r2.top;
END;
WITH ucr DO
BEGIN
inbuf := gd.pMin;
outbuf := pDecomp {altPtr};
huff_tree := @gt.hufftree;
pic_x := ge.rx;
midlen := gd.midSize {gd.minSize};
outbuflen := decompLen1 {altlen}; { interrupt point }
firstCall := TRUE;
lastCall := FALSE;
END;
WITH mpr DO
BEGIN
srcX := ge.x;
srcY := decompRows {ge.y};
dstRB := ge.rowbytes;
trans := ge.trans;
psrc := Ptr (ORD4(pDecomp) + ge.rx); { 1st row is unEOR hack }
pdst := pTrans; { [returns updated ptr] }
END;
max2 := 0;
WHILE NOT ucr.lastCall DO
BEGIN
decompRet := UncompH (@ucr);
max2 := max2 + decompRet;
IF ucr.lastCall THEN { final chunk probably has fewer rows ... }
mpr.srcY := decompRet DIV ge.x;
MonoPic (@mpr);
IF unroll THEN { blit new pic to screen incrementally }
BEGIN
r1.top := r1.bottom;
r1.bottom := r1.bottom + mpr.srcY {decompRows};
r2.top := r2.bottom;
r2.bottom := r2.bottom + mpr.srcY;
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
END;
END;
IF max2 <> gd.maxSize THEN { /should/ be exact pic size }
BEGIN
IF gfxDebug
THEN SysBeep (4);
{ EXIT (ReadGFXData); } { [but don't abort for this one] }
END;
IF NOT unroll THEN { blit new pic to screen, all at once }
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
(** debug_ticks := TickCount - debug_ticks; **) { time to decomp/display pic }
END;
{-------------------------------}
{ ShowGFXS }
{-------------------------------}
PROCEDURE Shrink75 (base: Ptr; cols100, cols75, rows75: INTEGER);
EXTERNAL; {68K}
PROCEDURE StipPic (srcX, srcY, { color, 1 byte/pixel, [clipped if necessary] }
dstRB: INTEGER; { mono rowBytes -- SRCX/4, ROUNDED UP & EVEN }
src, dst, maps: Ptr;
trans: BOOLEAN);
EXTERNAL; {68K}
{ display in Mono/Stipples (a section of) a picture, data in altbuf; position given in
pixel units. MAY need to shrink (0.75 scaling), MUST stipple (auto 2.00 scaling).
We now support incremental decompression/shrinkage/stippling/copybits, like the mono case.
This routine is much too convoluted, should be broken up somehow (split off the 75 percent
case?). }
PROCEDURE ShowGFXS (ypos, xpos: INTEGER);
VAR
r1, r2: rect;
{ pMax, }
pStips: Ptr;
shrink: BOOLEAN;
tx, ty, trb: INTEGER; { intermediate vals }
{ rawlen, }
stiplen: LONGINT; { length of stipple buffer }
sx, sy, srb: INTEGER; { final stipple vals }
pDecomp, { was pMax }
pTrans: Ptr;
max2, { fully decompressed size (actual) }
decompLen, { space available for decompression }
decompLen1, { ... rounded down to exact multiple of picx }
decompRet: LONGINT; { #bytes returned in latest decomp buffer }
decompRows, { #rows returned per UncompH call }
decompR75: INTEGER; { 75 percent of above }
transLen: LONGINT; { length of transparency buffer }
unroll: Boolean;
ucr: uncompRec;
mpr: MonoPicRec;
BEGIN
(* pMax := Ptr (ORD4(altPtr) + ge.rx); *) { start of byte data }
{ If stippling AND 1.50 scaling, reduce to 0.75 FIRST (so QD won't mung stips)
[if color, (would) never scale /down/, even for 1.50, just let QD expand]. }
shrink := {ge.fstip AND} NOT myBig;
ty := ge.y;
tx := ge.x;
trb := ge.rowBytes;
IF shrink THEN
{ recalc "internal" pic size, based on previously calc'ed display size }
BEGIN
ty := ge.dy DIV 2;
tx := ge.dx DIV 2;
{ new rowBytes must also be multiple of /4/, for in-place stippling }
trb := CalcRow4Bytes (tx, GFXAM_DEPTH);
END;
{ we now write into these 's' fields instead of the 'ge.' globals. This is because
if ShowGFXS is called twice in a row for the same picture, the globals got mashed.
[Removal of the (dead) 0.75 option would simplify things a lot.] }
sy := ge.dy; { set final (post-stip) vals }
sx := ge.dx;
srb := trb DIV 2;
(* sdepth := 1; *)
transLen := LongMult (sy, srb);
decompLen := altLen - (transLen + gd.minSize);
IF decompLen < DECOMP_MINLEN THEN { minSize was too big! }
BEGIN
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
EXIT (ShowGFXS);
END;
{ display strategy: if it's a "big" pic, then limit decompRows to a small number and
incrementally "unroll" the actual display, to avoid a long dead moment. The smaller
decompRows is, the smoother the unroll but the bigger the overhead.
}
unroll := TRUE; { default }
IF translen < (18000 DIV 4) THEN { is pic smaller than 1/4 screen? }
BEGIN
{ enough room to process in one pass? }
{ allow one extra line for unEOR, plus 128 bytes max for unRun spillover }
{ FOR STIPS, USE RAW LINE WIDTH HERE }
decompRows := (decompLen - (ge.x + 128)) DIV ge.x; { max poss value }
IF decompRows >= ge.y THEN { don't exceed actual pic height }
BEGIN
decompRows := ge.y;
decompR75 := ge.dy; { use previously-calc'ed value }
unroll := FALSE;
END;
END;
IF unroll THEN
BEGIN
decompRows := 2; { n=2 looks quite smooth }
decompR75 := 2;
IF shrink THEN
BEGIN
decompRows := 4; { SHRINK75 fails if smaller! }
decompR75 := 3;
END;
END;
decompLen1 := LongMult (decompRows, ge.x);
pTrans := altPtr;
pDecomp := Ptr (ORD4(altPtr) + transLen);
{ calc rects (for stips, always scaled 1.00) }
SetRect (r1, 0, 0, sx, sy);
r2 := r1;
OffsetRect (r2, wMarg + xpos, ypos);
{ if Transparent, blit in (entire) old pic first }
IF ge.trans THEN
CopyPic (@thePort^.portBits, BitMapPtr(@gfxPM), r2, r1);
IF unroll THEN
BEGIN
r1.bottom := r1.top; { setup for unroll }
r2.bottom := r2.top;
END;
WITH ucr DO
BEGIN
inbuf := gd.pMin;
outbuf := pDecomp {altPtr};
huff_tree := @gt.hufftree;
pic_x := ge.rx;
midlen := gd.midSize {gd.minSize};
outbuflen := decompLen1 {altlen}; { interrupt point }
firstCall := TRUE;
lastCall := FALSE;
END;
WITH mpr DO { for Stips, these are final display vals }
BEGIN
srcX := sx;
srcY := decompR75 {sy};
dstRB := srb;
trans := ge.trans;
psrc := Ptr (ORD4(pDecomp) + ge.rx); { 1st row is unEOR hack }
pdst := pTrans; { [returns updated ptr] }
END;
stipLen := LongMult (sy, srb);
(** rawlen {maxSize} := LongMult (ty, tx); { calc (shrunk) buflen; rows rounded DOWN }
IF (rawlen + ge.rx) + stipLen > altLen THEN { too big! }
BEGIN
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
EXIT (ShowGFXS); { avoid a crash }
END; **)
{ set gfxPM base to 2nd buffer }
pStips := Ptr (ORD4(altPtr) + (altLen - stipLen));
WITH gfxPM DO
BEGIN
baseAddr := pStips; { set gfxPM base to Stips base }
rowBytes := srb;
bounds := r1;
END;
max2 := 0;
WHILE NOT ucr.lastCall DO
BEGIN
decompRet := UncompH (@ucr);
IF shrink THEN
BEGIN
Shrink75 (mpr.psrc {pMax}, ge.rx, tx, decompR75 {ty}); { works w/ BYTES }
{ Clip75 ... alternate call }
END;
max2 := max2 + decompRet;
IF ucr.lastCall THEN { final chunk probably has fewer rows ... }
mpr.srcY := decompRet DIV ge.x;
{ stipple/pad to 2nd buffer }
StipPic (tx, decompR75 {ty}, trb DIV 2,
mpr.psrc {pMax}, mpr.pdst {pStips}, @gp.exstips, ge.trans);
(* MonoPic (@mpr); *)
IF unroll THEN { blit new pic to screen incrementally }
BEGIN
r1.top := r1.bottom;
r1.bottom := r1.bottom + mpr.srcY {decompRows};
r2.top := r2.bottom;
r2.bottom := r2.bottom + mpr.srcY;
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
END;
END;
IF max2 <> gd.maxSize THEN { /should/ be exact pic size }
BEGIN
IF gfxDebug
THEN SysBeep (4);
{ EXIT (ReadGFXData); } { [but don't abort for this one] }
END;
IF NOT unroll THEN { blit new pic to screen, all at once }
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
END;
{-------------------------------}
{ oShowGFXS }
{-------------------------------}
(** PROCEDURE Shrink75 (base: Ptr; cols100, cols75, rows75: INTEGER);
EXTERNAL; {68K}
PROCEDURE StipPic (srcX, srcY, { color, 1 byte/pixel, [clipped if necessary] }
dstRB: INTEGER; { mono rowBytes -- SRCX/4, ROUNDED UP & EVEN }
src, dst, maps: Ptr;
trans: BOOLEAN);
EXTERNAL; {68K} **)
{ display in Mono/Stipples (a section of) a picture, data in altbuf;
position given in pixel units }
{ MAY need to shrink (0.75 scaling), MUST stipple (auto 2.00 scaling) }
PROCEDURE oShowGFXS (ypos, xpos: INTEGER);
VAR
r1, r2: rect;
pMax, pStips: Ptr;
shrink: BOOLEAN;
tx, ty, trb: INTEGER; { intermediate vals }
rawlen,
stiplen: LONGINT; { length of stipple buffer }
sx, sy, srb: INTEGER; { final stipple vals }
BEGIN
pMax := Ptr (ORD4(altPtr) + ge.rx); { start of byte data }
{ If stippling AND 1.50 scaling, reduce to 0.75 FIRST (so QD won't mung stips)
[if color, (would) never scale /down/, even for 1.50, just let QD expand]. }
shrink := {ge.fstip AND} NOT myBig;
ty := ge.y;
tx := ge.x;
trb := ge.rowBytes;
IF shrink THEN
{ recalc "internal" pic size, based on previously calc'ed display size }
BEGIN
ty := ge.dy DIV 2;
tx := ge.dx DIV 2;
{ new rowBytes must also be multiple of /4/, for in-place stippling }
trb := CalcRow4Bytes (tx, GFXAM_DEPTH);
END;
{ we now write into these 's' fields instead of the 'ge.' globals. This is because
if ShowGFXS is called twice in a row for the same picture, the globals got mashed.
[Removal of the (dead) 0.75 option would simplify things a lot.] }
sy := ge.dy;
sx := ge.dx;
srb := trb DIV 2;
(* sdepth := 1; *)
(** ge.y := ge.dy; { and set final (post-stip) vals }
ge.x := ge.dx;
ge.depth := 1; { adjust for depth=1 }
ge.rowbytes := trb DIV 2; **)
{ calc rects (for stips, always scaled 1.00) }
SetRect (r1, 0, 0, sx, sy);
r2 := r1;
OffsetRect (r2, wMarg + xpos, ypos);
(* IF ge.fstip THEN *)
IF shrink THEN
BEGIN
Shrink75 (pMax, ge.rx, tx, ty); { works w/ BYTES }
{ Clip75 ... alternate call }
END;
rawlen {maxSize} := LongMult (ty, tx); { calc (shrunk) buflen; rows rounded DOWN }
stipLen := LongMult (sy, srb);
IF (rawlen + ge.rx) + stipLen > altLen THEN { too big! }
BEGIN
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
EXIT (oShowGFXS); { avoid a crash }
END;
{ set gfxPM base to 2nd buffer }
pStips := Ptr (ORD4(altPtr) + (altLen - stipLen));
WITH gfxPM DO
BEGIN
baseAddr := pStips; { set gfxPM base to Stips base }
rowBytes := srb;
bounds := r1;
END;
{ if Transparent, first blit in old pic }
IF ge.trans THEN
CopyPic (@thePort^.portBits, BitMapPtr(@gfxPM), r2, r1);
{ stipple/pad to 2nd buffer }
StipPic (tx, ty, trb DIV 2,
pMax, pStips, @gp.exstips, ge.trans);
(* END { IF ge.fstip THEN } *)
{ blit new pic out to screen }
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
END;
{-------------------------------}
{ ShowGFX }
{-------------------------------}
PROCEDURE SqzRow (src, dst: Ptr; rowPix {,rowBytes, rows}: INTEGER);
EXTERNAL; {68K}
{ display in Color (a section of) a picture, data in altbuf;
position given in pixel units }
PROCEDURE ShowGFX{Color} (ypos, xpos: INTEGER);
VAR
r1, r2: rect;
pMax, pFinal: Ptr;
n: INTEGER;
p1, p2: Ptr;
BEGIN
IF ge.mono THEN { neither stipple NOR scale }
BEGIN
ShowGFXM (ypos, xpos);
EXIT (ShowGFX);
END;
IF ge.fstip THEN { handle the stipple case separately, too }
BEGIN
ShowGFXS (ypos, xpos);
EXIT (ShowGFX);
END;
pMax := Ptr (ORD4(altPtr) + ge.rx); { start of byte data }
SetRect (r1, 0, 0, ge.x, ge.y);
SetRect (r2, 0, 0, ge.dx, ge.dy); { always scaled up 2.00 }
OffsetRect (r2, wMarg + xpos, ypos);
IF TRUE {NOT ge.fstip} THEN
BEGIN
pFinal := altptr;
p1 := pMax; { start of byte data }
p2 := pFinal; { start of nibble data }
{ Squeeze bytes (16-color pixels) into nibbles, throwing away the high nibbles.
also shift picture to base of buffer, and pad each row }
FOR n := 1 TO ge.ry DO
BEGIN
SqzRow (p1, p2, ge.rx);
p1 := Ptr (ORD4(p1) + ge.rx); { advance ptrs }
p2 := Ptr (ORD4(p2) + ge.rowBytes); { skip/pad }
END;
END;
WITH gfxPM DO
BEGIN
baseAddr := pFinal; { set gfxPM base to buffer base }
rowBytes := ge.rowBytes;
bounds := r1;
IF TRUE {NOT ge.fstip} THEN
{ adjust some additional fields }
BEGIN
{ mark as a pixmap [otherwise, /always/ 1 plane] }
rowBytes := rowBytes + $8000;
END;
END;
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
(** IF n = -999 THEN { for testing only -- see if this munges colors ... }
BEGIN
CopyPic (@thePort^.portBits, BitMapPtr(@gfxPM), r2, r1);
CopyPic (BitMapPtr(@gfxPM), @thePort^.portBits, r1, r2);
END; **)
END;
{-------------------------------}
{ EraseGFX }
{-------------------------------}
{ clear a picture, position given in pixel units }
PROCEDURE EraseGFX (ypos, xpos: INTEGER);
VAR
r: rect;
BEGIN
SetRect (r, 0, 0, ge.dx, ge.dy);
OffsetRect (r, wMarg + xpos, ypos);
EraseRect (r);
END;
{-------------------------------}
{ opDisplay }
{-------------------------------}
{ position given in pixel units, relative to current window, 0-origin }
PROCEDURE opDisplay (id, ypos, xpos, erase: INTEGER);
VAR
r: rect;
clip: Boolean;
BEGIN
IF ReadGFXEntry (id) <> noErr THEN { call BEFORE erase or dy/dx refs! }
EXIT (opDisplay);
IF (ypos < -2) OR (xpos < -2) THEN { ERROR, force inbounds }
BEGIN
ypos := 0;
xpos := 0;
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
END;
{ -2 (after 0-origining) means only read palette/stipple info }
IF (ypos = -2) OR (xpos = -2) THEN
BEGIN
{ to be implemented }
EXIT (opDisplay);
END;
{ -1 (after 0-origining) means use current position }
IF ypos = -1
THEN ypos := curRow
ELSE ypos := firstRow + ypos; { relative to current window }
IF xpos = -1
THEN xpos := curCol
ELSE xpos := firstCol + xpos;
(** IF id > NPICS THEN
EXIT (opDisplay); **) { no errs for now }
{ check size, clip to logical window (YZIP) if too big }
IF (ge.dy > lastRow - ypos {AFTER relativizing})
OR (ge.dx > lastCol - xpos {AFTER relativizing}) THEN
BEGIN
GetClip (clipRgn1); { save old }
r.top := firstRow;
r.left := firstCol;
r.bottom := lastRow;
r.right := lastCol;
ClipRect (r);
clip := TRUE;
IF gfxDebug { signal an error ... }
THEN SysBeep (4);
END
ELSE clip := FALSE;
IF erase <> 0 THEN { clear the (current) pic }
EraseGFX (ypos, xpos)
ELSE
BEGIN
IF ReadGFXPalette = noErr THEN
BEGIN
(* CheckMyUpdate; *)
IF ReadGFXData = noErr THEN
ShowGFX (ypos, xpos);
END;
END;
IF clip THEN
SetClip (clipRgn1); { undo the above }
END;
{-------------------------------}
{ opPicinf }
{-------------------------------}
{ Fill in given table with size (in pixels) of the given picture.
If invalid id, return negative result }
TYPE
ARRAY2 = ARRAY[1..2] OF INTEGER;
FUNCTION opPicinf (id: INTEGER; VAR tbl: ARRAY2): INTEGER;
BEGIN
opPicinf := 0; { assume no error }
IF id = 0 THEN { >> DEAD IN YZIP (supposedly) << }
BEGIN
tbl[1] := gh.npics; { [should be /highest/ id, actually] }
EXIT (opPicinf);
END;
{ otherwise, read just enough to return size }
IF ReadGFXEntry (id) = noErr THEN
BEGIN
tbl[1] := ge.dy {DIV lineheight};
tbl[2] := ge.dx {DIV colWidth};
END
ELSE
BEGIN
opPicinf := -1;
{ provide some defaults for games that don't trap errors ... }
tbl[1] := 2*lineHeight;
tbl[2] := 2*colWidth;
END;
END;
{-------------------------------}
{ getPicset }
{-------------------------------}
{ Preload the requested set of pics (or as many as will fit)
Note: PICSET currently caches /compressed/ picture data, trading off space against
display speed (would reverse this to do animation, for example). Also, palette data
is not touched by the caching. This is okay for most small pics since they share
someone else's palette.
The format of the info at psPtr is as follows (all word entries -- must change off/len
to longs if PSDEFLEN ever exceeds 32K!):
- #ids in picset
- 1st id
- starting offset (in bytes, relative to psData)
- length (in bytes)
- 2nd id, etc ... }
PROCEDURE getPicset (tbl: WPtr; setCount: INTEGER);
VAR
p1: WPtr;
p2: Ptr;
curCount, id: INTEGER;
headerLen, off,
avail, { space remaining in picset buffer }
curSize: LONGINT;
FUNCTION psRead: osErr;
BEGIN
psRead := -1; { default result }
IF avail <= 0
THEN EXIT (psRead);
{ get dataOff (write new routine? this one does some extra work) }
IF ReadGFXEntry (id) <> noErr
THEN EXIT (psRead);
{ read in the length of the (compressed) data }
IF FSReadClr (ge.dataOff, gh.LBYTES, @curSize, 4) <> noErr
THEN EXIT (psRead);
IF curSize > MAXITEMLEN THEN { exceeds our absolute max }
EXIT (psRead); { [picset isn't for full screens] }
IF curSize > avail THEN { not enough room left! }
BEGIN
IF avail < 256 THEN { avoid further disk hits; }
avail := 0; { return errs for remaining pics in set }
EXIT (psRead);
END;
{ read (compressed) data into the picset buf }
IF FSReadGFX (-1, curSize, p2) <> noErr
THEN EXIT (psRead);
psRead := 0; { all okay }
END;
BEGIN
IF ps.psInited <> PSINITMARK { valid buffer? }
THEN EXIT (getPicset);
p1 := WPtr (ORD4(ps.psPtr) + 2); { reserve 1 word for #ids }
headerLen := 2 + (PSENTRYLEN * setCount);
p2 := Ptr (ORD4(ps.psPtr) + headerLen);
ps.psData := p2; { data starts here, save ptr }
avail := ps.psLen - headerLen;
IF avail <= 0
THEN EXIT (getPicset);
curCount := 0;
WHILE setCount > 0 DO
BEGIN
{ get next id in set }
id := GetWord {GetWordB} (tbl); { now guaranteed even-aligned ... }
IF psRead = noErr THEN
BEGIN
PutWord (p1, id);
off := ORD4(p2) - ORD4(ps.psData);
PutWord (p1, off); { picbuf offset }
{ PutLong (LPtr(p1), off); }
PutWord (p1, curSize); { length (in bytes) }
{ PutLong (LPtr(p1), off); }
curSize := RoundUp (curSize); { KEEP p2 EVEN ALIGNED, FOR I/O SPEED }
p2 := Ptr (ORD4(p2) + curSize);
avail := avail - curSize;
curCount := curCount + 1;
END
ELSE
{ if any error, write nothing to header }
BEGIN
{ if debug then Sysbeep (4); }
END;
setCount := setCount - 1;
END;
p1 := WPtr (ps.psPtr); { [use local var, don't bump global!] }
PutWord (p1, curCount); { store the success count }
END;
{-------------------------------}
{ searchPicset }
{-------------------------------}
{ Check the current picset for the given id;
return zero if found (and return ptr/len in globals) }
FUNCTION searchPicset (id: INTEGER): osErr;
VAR
p1: WPtr;
setCount: INTEGER;
temp: INTEGER;
BEGIN
searchPicset := -1; { default }
IF ps.psInited <> PSINITMARK
THEN EXIT (searchPicset);
p1 := WPtr (ps.psPtr);
setCount := GetWord (p1); { id/off/len triples follow }
WHILE setCount > 0 DO
BEGIN
IF GetWord (p1) = id THEN { found it, return info }
BEGIN
{ CAUTION: the following two items get sign-extended.
Could save off/len as LONGS instead }
ps.p := Ptr (GetWord (p1) + ORD4(ps.psData));
ps.len := GetWord (p1);
searchPicset := 0; { success }
EXIT (searchPicset);
END
ELSE
BEGIN
p1 := WPtr (ORD4(p1) + (PSENTRYLEN-2)); { skip to next triple }
setCount := setCount - 1;
END;
END;
END;
{-------------------------------}
{ InitGFX }
{-------------------------------}
{ open gfx megafile, also alloc mem and read in directory }
FUNCTION InitGFX (start: BOOLEAN): OSErr;
VAR
err: osErr;
gfxName: STR255;
BEGIN
InitGFX := -1; { default }
gh.inited := FALSE;
dirPtr := NIL;
IF start THEN
BEGIN
gh.npics := 0; { [default val, for PICINF] }
{ for a small window use mono gfx, for a big window use color gfx (if big mono,
we currently stipple -- should probably open a small window instead ) }
IF myBig
THEN gfxName := 'CPic.Data'
ELSE gfxName := 'Pic.Data';
IF SearchOpen ('', gfxName, gfxChan) <> 0 THEN
EXIT (InitGFX);
{ read [id and] global flags }
IF FSReadGFX (-1, 2, @gh.gflags) <> noErr
THEN EXIT (InitGFX);
{ read global huff ptr (if any) }
IF FSReadClr (-1, 2, @gh.ghuffOff, 4) <> noErr
THEN EXIT (InitGFX);
gh.ghuffOff := 2 * gh.ghuffOff; { make byte offset }
{ read local [and extern] pic counts }
IF FSReadGFX (-1, 4, @gh.npics) <> noErr
THEN EXIT (InitGFX);
{ read length of each directory entry (12-14-16, etc) }
IF FSReadClr (-1, 1, @gh.entryLen, 2) <> noErr
THEN EXIT (InitGFX);
(** { read graphics checksum } { NEED TO SKIP A BYTE AFTER ENTRYLEN }
IF FSReadGFX (-1, 2, @gh.cksum) <> noErr
THEN EXIT (InitGFX);
**)
{ read graphics version byte }
IF FSReadClr (12, 1, @gh.version, 2) <> noErr
THEN EXIT (InitGFX);
IF gh.version >= 1
THEN gh.LBYTES := 3
ELSE gh.LBYTES := 2; { this case just for backward compatibility }
{ alloc directory buffer, and read in directory
pro: avoids disk hits for PICINF (and PICDISP prep)
300 pics/file => 8 hits/pic (binary search)
30 pics/set => 30*8 hits
con: 300 pics/file * 16 bytes => 4.8K mem
disk hits reduced by caching (newer Systems), 30*8 -> ~8
}
gh.dirLen := gh.npics * gh.entryLen;
dirPtr := NewPtr (gh.dirLen);
IF dirPtr = NIL
THEN EXIT (InitGFX);
IF FSReadGFX (GHEADLEN, gh.dirLen, dirPtr) <> 0
THEN EXIT (InitGFX);
gp.pOff := 0; { mark these recs as empty }
gt.hOff := 0;
gh.inited := TRUE; { init successful }
{ and init picset stuff (a failure here is not currently reported) }
WITH ps DO
BEGIN
psLen := PSDEFLEN;
psPtr := NewPtr (psLen); { separate buffer }
IF psPtr <> NIL THEN
BEGIN
WPtr(psPtr)^ := 0; { start with an empty set }
psInited := PSINITMARK; { success }
END;
END;
{ finally, show a boot screen, if requested & exists [NOT IN SPEC] }
(** IF BAND (gh.gflags, GF_SPLASH) <> 0 { check flag }
THEN opDisplay (1, 0, 0, 0); { show 1st pic (center it?) }
**) END
ELSE { cleanup }
BEGIN
IF gfxChan <> 0 THEN { this IS necessary under MultiFinder }
BEGIN
InitGFX := FSClose (gfxChan);
gfxChan := 0;
END;
(** IF dirPtr <> NIL THEN { NOT NECESSARY? [nor for 68K allocs] }
BEGIN
DisPosPtr (dirPtr);
dirPtr := NIL;
END;
IF ps.psPtr <> NIL THEN
BEGIN
DisPosPtr (ps.psPtr);
ps.psPtr := NIL;
END;
**)
END;
InitGFX := 0; { all okay }
END;