// PreScan.bcpl
//
// modified by Ramshaw, March 30, 1981 4:39 PM
// - add switch for Bob Hunt, to make CoordsConvertBox either
// behave like it should (see below) or else try for local fidelity
// so that SIL line drawings look better on a low-resolution printer.
//
// modified by Ramshaw, March 11, 1981 10:54 AM
// - change CoordsConvertBox to make rectangles behave just
// like the corresponding objects.
//
// modified by Ramshaw, December 11, 1980 12:45 PM
// - make sure that CoordsConvertBox always returns a box that is
// at least one bit in each dimension, since all kinds of other
// code has its termination checks at the END of loops (instead
// of at the beginning where they belong).
//
// modified by Butterfield, October 13, 1980 3:35 PM
// - ResolutionB, ResolutionS, 1X instead of 10X - 10/13/80
//
// errors 800
//
//Prescan phase of press 3100 routine
//
//PreScanInit()
//PreScanClose()
//Called to begin and end pre scan pass.
//ShowPage(pg,part)
//Called to pre-scan a page. pg is a PageG structure
//that will be appropriately filled up. part is a pointer to
//a PE describing the part of the file.
//ShowEntity(e)
//Called internally to process each entity
//
//CoordsUpdate()
//Called if CoordsInvalid to update CurSCoord,CurBCoord.
//CoordsConvert(x,y,lvS,lvB,absolute,changed)
//Given coordinate (x,y), convert it to (double precision) bit
//coordinates (lvS,lvB). If absolute, the x,y pair is absolute,
//otherwise relative. Changed is optional--a set of flags to say
//which of the coordintes have changed:
//2X changed -- only update corresponding bit coord
//1Y changed -- "
//CoordsConvertF(lvx,lvy,acS,acB,absolute)
//Called to convert floating point numbers (lvx,lvy) to bit coordinates.
//Results are left in AC’s (acS,acB).
//CoordsConvertBox(width,height,lvSmin,lvSmax,lvBmin,lvBmax)
//Returns true if rectangular thing fits on page -- S,B values filled.
//CoordsBound(sl,sr,bl,br)
//Four numbers are a box around something to be displayed.
//Expand bounding box for page. Returns true if thing is
//OK (i.e., will lie entirely within clipping area)
get "pressinternals.df"
get "pressparams.df"
get "pressfile.df"
// outgoing procedures
external
[
PreScanInit
PreScanClose
ShowPage
CoordsUpdate
CoordsConvert
CoordsConvertBox
CoordsConvertF
CoordsBound
]
// outgoing statics
external
[
PreScratchW//Window on scratch file
PreGodW//Window on god file
DL//Window on DL of press file
ScratchLen//Length of scratch file
Entity//Entity being processed
CurSCoord//Current S and B coordinates
CurBCoord// (double precision integer,fraction)
CoordsInvalid//Flags of which coords set since before
CurSMax//Bounding box
CurSMin
CurBMax
CurBMin
PSStats//Statistics record (structure PSStat)
SimplePage//True if only characters on page
//Spruce-like font load stuff
ICCUses
FontSizePageNew
FontSizePageOld
maxBandRecsSoFar
maxFontSizeSoFar
nFontLoads
maxPrintPassRecs
]
static
[
PreScratchW
PreGodW
DL
ScratchLen
Entity
CurSCoord
CurBCoord
CoordsInvalid
CurSMax
CurSMin
CurBMax
CurBMin
PSStats
SimplePage
//Spruce-like font load stuff
ICCUses
FontSizePageNew
FontSizePageOld
maxBandRecsSoFar
maxFontSizeSoFar
nFontLoads
maxPrintPassRecs
]
// incoming procedures
external
[
//Related PRESCAN procedures
ShowCharsInit
ShowCharsClose
ShowChars
ShowCharsSet
ShowCharsFont
ShowCharsSetSpace
PreFSGet
ShowObject
ShowRectangle
ShowDots
//Band writing procedures
BandWriteInit
BandWriteClose
BandWriteBeginPage
BandWriteEndPage
BandWrite
BandOnCopy
BandSync
BandRecords
//WINDOW
WindowInit
WindowClose
WindowRead
WindowReadBlock
WindowReadByte
WindowRead2Bytes
WindowSetPosition
//PARTS
SetPartBounds
SetPositioninPart
SetBytePositioninPart
GetPositioninPart
SkipinPart
//FONTMAKE
MakeFI
FontMakeup
//PRESS
PressError
FSGetX
FSGet
FSPut
DblShift
GetTime
//PRESSML
DoubleAdd; DoubleSub; DoubleCop
TGr
//OS
MoveBlock
SetBlock; Zero
//FLOAT
DPAD; DPSB; FNEG
FSTDP; FLD; FST; FLDI; FAD; FSB; FML; FDV
//CURSOR
CursorChar
CursorDigit
CursorToggle
//METER
MeterBlock
MeterTime
]
// incoming statics
external
[
EL//Window on EL of press file
PressFile//Files to use...
BandFile
BandWindow
ScratchFile
GodFile
XOffset
YOffset
ScaleOffset
SoftScan
LocalFidelity
//color stuff from PreBands
BandCurHue
BandCurSat
BandCurBright
nPrinterColors
ResolutionS
ResolutionB
portrait
nScanLines
nBitsPerScan
Report
ICCtot
PermanentBottom
OverlayBottom
nPages
FA
]
// internal statics
static
[
CopyUsed//True if <OnlyOnCopy> used
ColorUsed//True if <SetHue>,<SetSaturation> used
MicaX//Last <set x>
MicaY//Last <set y>
ResFactorS//Multiplier for S resolution
ResFactorB//Multiplier for B resolution (micas=>bits)
]
// File-wide structure and manifest declarations.
structure EHC ://EH + some stuff
[
@EH
nextword//Pointer to next entity
ELCPosword 2//Part pos of entity commands
]
// Procedures
let PreScanInit() be
[ compileif ReportSw then [ GetTime(lv Report>>REP.PreTime) ]
CursorChar($S)
DL=WindowInit(PressFile,1)//Windows for reading it.
PreScratchW=WindowInit(ScratchFile,1)
PreGodW=WindowInit(GodFile,1)
//Set up coordinates and conversion data
let a=FSGetX(10)//Grumble
CurSCoord=a
CurBCoord=a+2
ResFactorS=a+4
ResFactorB=a+6
FLDI(1, 2540);
if ScaleOffset then [ FLDI(0,ScaleOffset); FDV(1,0) ]
FLDI(0,ResolutionS); FDV(0,1); FST(0,ResFactorS)
FLDI(0,ResolutionB); FDV(0,1); FST(0,ResFactorB)
if portrait then [ let t=XOffset; XOffset=YOffset; YOffset=t ]
//Get length of scratch file (will go away someday...).
ScratchLen=a+8
WindowReadBlock(PreScratchW,ScratchLen,2)
//Now, compute number of records available to printing pass
let csiz=PermanentBottom-OverlayBottom-nPages*(size PageG/16+size FontG/16)
//what is this stuff??????????????? csiz=csiz-(OverlayTable!2-OverlayTable!1)*256
maxPrintPassRecs=csiz rshift BandFile>>F.LogPagesize
let icc=ICCtot
ICCUses=FSGetX(icc)
Zero(ICCUses,icc) //0=>not assigned
nFontLoads=0
maxFontSizeSoFar=4 //Account for dummy character
maxBandRecsSoFar=0
BandWriteInit()//Start up band writing
ShowCharsInit()//And character fonts
]
and PreScanClose() be
[
WindowClose(DL)
BandWriteClose()//doesn’t close BandWindow
MakeFI()
FSPut(ICCUses)
ShowCharsClose()
FontMakeup()
WindowClose(BandWindow)
WindowClose(PreScratchW)
WindowClose(PreGodW)
FSPut(CurSCoord)
compileif ReportSw then [ GetTime(lv Report>>REP.PreTime) ]
]
and ShowPage(pg,part) be
[ CursorDigit(pg>>PageG.PageNumber)
let PsRec=vec size PSStat/16
compileif MeterSw then
[ PSStats=PsRec; Zero(PsRec,size PSStat/16)
PSStats>>PSStat.TimeIn=MeterTime()
]
let frec=part>>PE.pStart//First record
let nrec=part>>PE.pRecs
SetPartBounds(EL,frec,nrec)//Limit the EL
SetPartBounds(DL,frec,nrec)
let t=vec 1
t!0=0; t!1=nrec
DblShift(t,-LogPressRecordSize)//Length of page part
let s=vec 1
s!0=0; s!1=part>>PE.Padding+1//Prepare to read length entry
DoubleSub(t,s)// t is pos in part
SetPositioninPart(EL,t)
FontSizePageOld=0
FontSizePageNew=0
//Now read all entities
let Elist=0
[
let Elen=vec 1
Elen!0=0; Elen!1=WindowRead(EL)
if Elen!1 eq 0 then break//Last entity has length 0
compileif MeterSw then
[ PSStats>>PSStat.Entities=PSStats>>PSStat.Entities+1 ]
let c=vec 1
GetPositioninPart(EL,c)// c => just beyond entity
let p=PreFSGet(size EHC/16)
p>>EHC.next=Elist//Chain new one on list
Elist=p
let d=vec 1
DoubleCop(d,c)
DoubleSub(d,table [ 0;size EH/16 ]) // d => beginning of EH
SetPositioninPart(EL,d)//At beginning of EH
WindowReadBlock(EL,p,size EH/16) //Read EH
DoubleSub(c,Elen)// c is head of entity commands
DoubleCop(lv p>>EHC.ELCPos,c)//Save it
DoubleSub(c, table [ 0;1 ])//Next length position
SetPositioninPart(EL,c)
] repeat
//Initialize bounding box.
CurSMin=nScanLines+1; CurSMax=-1
CurBMin=nBitsPerScan+1; CurBMax=-1
CopyUsed=false
SimplePage=not SoftScan//true unless we’re not using ORbit
ColorUsed=false
//Start off bands
BandWriteBeginPage()
//Process all entities in order
while Elist do
[
CursorToggle(0)
SetPositioninPart(EL,lv Elist>>EHC.ELCPos)
ShowEntity(Elist)//Go interpret the entity
let n=Elist>>EHC.next
FSPut(Elist)
Elist=n
]
//Compute area of page used
let bm=CurBMin&(-16)//Margin offsets are all in "words"
let bc=(CurBMax-bm+32)/32//Count of double words
pg>>PageG.BitMargin=bm
pg>>PageG.BitWc=bc*2
pg>>PageG.CopyCrucial=CopyUsed
pg>>PageG.SimplePage=SimplePage
pg>>PageG.ColorUsed=ColorUsed
//Fill in min and max bands used. The reason for divides rather than
// shifts in this code is that if CurSMax is still -1 (no stuff entered),
// we wish PageG.FirstBand to be greater than PageG.LastBand
pg>>PageG.FirstBand=CurSMin/BANDWidth
pg>>PageG.LastBand=CurSMax/BANDWidth
//And finish off the bands.
BandWriteEndPage(pg)
compileif MeterSw then
[
MoveBlock(lv PSStats>>PSStat.PG,pg,size PageG/16)
PSStats>>PSStat.TimeOut=MeterTime()
MeterBlock(METERShowPage,PSStats,size PSStat/16)
]
// If this page can be folded into the last one (for purposes of
// font planning), it must be the cas
// that 2*maxBandRecs+nFontRecs is small enough to fit
// (le maxPrintPassRecs)
let m=maxBandRecsSoFar//not including this page
let nBandRecs=pg>>PageG.nRecords
let nFontRecs=BandRecords(maxFontSizeSoFar+FontSizePageNew+ICCtot)
// If it does not fit, push out the earlier font load
if nFontRecs+m*2 gr maxPrintPassRecs then
[ if maxBandRecsSoFar then MakeFI() //push out current font load
maxFontSizeSoFar=FontSizePageOld+4
for i=0 to ICCtot-1 do if ICCUses!i gr 0 then ICCUses!i=0
maxBandRecsSoFar=0
]
for p=pg to pg+(size PageG/16)*(nPrinterColors-1) by size PageG/16 do
p>>PageG.fontLoad=nFontLoads
for i=0 to ICCtot-1 do if ICCUses!i ls 0 then ICCUses!i=1
maxFontSizeSoFar=maxFontSizeSoFar+FontSizePageNew
if nBandRecs gr maxBandRecsSoFar then maxBandRecsSoFar=nBandRecs
]
and ShowEntity(e) be
[
Entity=e
//Set up for reading DL for this entity
SetBytePositioninPart(DL,lv e>>EH.Dstart)//Position DL
//Coordinate defaults
MicaX=0
MicaY=0
CoordsInvalid=3//Say that both coords are wrong
let AlternativeDone=false//not inside Alternative selection initially
//Color and copy defaults
BandSync(0,0,0)//Default color
BandOnCopy(0)
//Font defaults
ShowCharsSet(e>>EH.Fontset)//Set
ShowCharsFont(0)
//Reset-space
ShowCharsSetSpace(0)
EL>>W.ByteCount=-(e>>EH.Length-(size EH/16))*2
while EL>>W.ByteCount ls 0 do
[
CursorToggle(1)
let Com=WindowReadByte(EL)
test Com le EShortMax then
switchon Com rshift 3 into
[
//Show characters short: Com is # of characters - 1
case EShowShort/8:
case EShowShort/8+1:
case EShowShort/8+2:
case EShowShort/8+3:
ShowChars(Com+1)
endcase
//Skip characters short: Com% is #of characters - 1
case ESkipShort/8:
case ESkipShort/8+1:
case ESkipShort/8+2:
case ESkipShort/8+3:
for ch=1 to (Com%)+1 do
WindowReadByte(DL)
endcase
//Show characters and skip one: Com% is number-1
case EShowSkip/8:
case EShowSkip/8+1:
case EShowSkip/8+2:
case EShowSkip/8+3:
ShowChars((Com%)+1)
WindowReadByte(DL)
endcase
//Set space x&y short: (Com+new byte)䕱 is length
case ESpaceXShort/8:
case ESpaceYShort/8:
[
let oth=WindowReadByte(EL)
oth=oth+(Com&3) lshift 8
ShowCharsSetSpace( (((Com rshift 3) eq ESpaceXShort/8)? 1,2),oth)
]
endcase
//Font change
case EFont/8:
case EFont/8+1:
ShowCharsFont(Com)
endcase
default: endcase
]
or
switchon Com into
[
//Alternative: followed by 3 words: mask, ELbytes, DLbytes
case EAlternative:
[ let mask=WindowRead2Bytes(EL)
let ELbytes,DLbytes=vec 1,vec 1
ELbytes!0=WindowRead2Bytes(EL)
ELbytes!1=WindowRead2Bytes(EL)
DLbytes!0=WindowRead2Bytes(EL)
DLbytes!1=WindowRead2Bytes(EL)
if (mask+ELbytes!0+ELbytes!1+DLbytes!0+DLbytes!1) eq 0 then
[ AlternativeDone=false//no more alternatives to skip
endcase
]
if AlternativeDone then
[ if (ELbytes!0 ne 0)%(ELbytes!1 ls 0) then PressError(804)
EL>>W.ByteCount=EL>>W.ByteCount+ELbytes!1
SkipinPart(EL,3,ELbytes)
SkipinPart(DL,3,DLbytes)
endcase
]
//should check here to make sure we can do next alternative
//but since this is the all powerful program, we must be ok
AlternativeDone=true
]
endcase
//OnlyOnCopy: next byte is copy number
case EOnlyOnCopy:
[
BandOnCopy(WindowReadByte(EL))
CopyUsed=true
]
endcase
//Set x: next word is new x as signed integer
case ESetX:
[
MicaX=WindowRead2Bytes(EL)
CoordsInvalid=CoordsInvalid%2//X changed
]
endcase
//Set y: next word is new y as signed integer
case ESetY:
[
MicaY=WindowRead2Bytes(EL)
CoordsInvalid=CoordsInvalid%1//Y changed
]
endcase
//Show characters: next entity byte is # of characters
case EShow:
ShowChars(WindowReadByte(EL))
endcase
//Skip characters: next entity byte is number
case ESkip:
SkipinPart(DL,0,WindowReadByte(EL))
endcase
//Skip control bytes: skip next three bytes
case ESkipControlImmediate:
for i=1 to WindowReadByte(EL) do WindowReadByte(EL)
endcase
case ESkipControl:
[
SkipinPart(DL,0,WindowRead2Bytes(EL))
WindowReadByte(EL)//Type of control info
]
endcase
//Show character immediate
case EShowImmediate:
ShowChars(1,WindowReadByte(EL))
endcase
//Set space x
case ESpaceX:
ShowCharsSetSpace(1,WindowRead2Bytes(EL))
endcase
//Set space y
case ESpaceY:
ShowCharsSetSpace(2,WindowRead2Bytes(EL))
endcase
//Reset-space
case EResetSpace:
ShowCharsSetSpace(4)
ShowCharsSetSpace(0)
endcase
//Space
case ESpace:
ShowChars(1,#40)
endcase
//Brightness,Hue,Saturation
case ESetBright:
BandSync(BandCurHue,BandCurSat,WindowReadByte(EL))
ColorCheck()
endcase
case ESetHue:
BandSync(WindowReadByte(EL),BandCurSat,BandCurBright)
ColorCheck()
endcase
case ESetSat:
BandSync(BandCurHue,WindowReadByte(EL),BandCurBright)
ColorCheck()
endcase
//Show object
case EShowObject:
SimplePage=SimplePage&ShowObject(WindowRead2Bytes(EL))
endcase
//Show dots (two flavors)
case EShowDots:
case EShowDotsOpaque:
[
let c=vec 1
c!0=WindowRead2Bytes(EL)
c!1=WindowRead2Bytes(EL)
ShowDots(c,Com eq EShowDotsOpaque)
SimplePage=false
]
endcase
//Show rectangle (rule)
case EShowRectangle:
[
ShowRectangle(WindowRead2Bytes(EL),
WindowRead2Bytes(EL))
]
endcase
//Nop
case ENop:
endcase
default:
PressError(801,Com)
endcase
]//switchon
]//while loop
if EL>>W.ByteCount ne 0 then PressError(802)
ShowCharsSetSpace(4)//Put widths back in font
]
and ColorCheck() be
[ if (BandCurSat ne 0)&(BandCurBright ne 0) then ColorUsed=true
if ((BandCurHue rem 40) ne 0)%
((BandCurSat ne 0)&(BandCurSat ne 255))%
((BandCurBright ne 0)&(BandCurBright ne 255)) then SimplePage=false
]
// COORDINATE STUFF
and CoordsUpdate() be
[
let x=MicaX+Entity>>EH.Xe
let y=MicaY+Entity>>EH.Ye
CoordsConvert(x,y,CurSCoord,CurBCoord,true,CoordsInvalid)
CoordsInvalid=0
]
and CoordsConvert(x,y,lvS,lvB,absolute,whochanged; numargs n) be
[
if n eq 5 then whochanged=3
if whochanged eq 0 then PressError(803)
if portrait then [ whochanged=(table [ 0;2;1;3 ] )!whochanged
let t=x; x=y; y=t ]
if absolute then [ x=x+XOffset; y=y+YOffset ]
if (whochanged&2) ne 0 then
[
FLDI(0,x); FML(0,ResFactorS)//X scaled
if portrait then FNEG(0)
FSTDP(0,lvS)//X changed
if portrait&absolute then lvS!0=nScanLines-1+lvS!0
]
if (whochanged&1) ne 0 then
[
FLDI(0,y); FML(0,ResFactorB)//Y scaled
FSTDP(0,lvB)//Y changed
if absolute then lvB!0=lvB!0+(FA lshift 4)//add FA for ORbit
]
]
and CoordsConvertF(lvx,lvy,acS,acB,absolute) be
[
if portrait then [ let t=lvx; lvx=lvy; lvy=t ]
FLD(acS,lvx)
if XOffset & absolute then [ FLDI(0,XOffset); FAD(acS,0) ]
FML(acS,ResFactorS)
if portrait then
[
FNEG(acS)
if absolute then [ FLDI(0,nScanLines-1); FAD(acS,0) ]
]
FLD(acB,lvy)
if YOffset & absolute then [ FLDI(0,YOffset); FAD(acB,0) ]
FML(acB,ResFactorB)
if absolute&FA then [ FLDI(0,FA lshift 4);FAD(acB,0)]
]
// Locate a "box" with given width and height relative to the
// current location, and return its S,B dimensions. Returns
// "true" if thing fits on page.
// There is rather careful handling of the box to try to calculate
// a somewhat larger box than the user asks for. The reason is
// that we wish to insure two things:
//1. Rectangles that are supposed to meet actually do so,
// in spite of roundoff error.
//2. On low resolution devices, rectangles with the same W
// or H have the same widths or heights, in spite of their
// alignment with the coordinate grid.
and CoordsConvertBox(w,h,lvSmin,lvSmax,lvBmin,lvBmax) = valof
[
let GetMinMax(cur,delta,lvMin,lvMax) be
// The dimension of the rectangle is "d". When delta>0, this is
// derived by Floor(delta+1/2). When delta<0, we want Floor(-delta+1/2),
// which is -Floor(delta+1/2) because Floor(x)=-Floor(-x+1-epsilon).
//JTM note: while this argument may or may not be correct, the sad truth is that
//taking the left half of a negative double word integer is not the Floor
//but rather the Ceiling. So, change the above to be Ceiling(delta-1/2)
// Also: e has been rounded down (not ok if negative delta)
// Max is e+d, which may be short if e has been rounded down
//let half= table [ 0; #100000 ]
//test delta!0 ge 0 then
//[ DoubleAdd(delta,half)
// @lvMin=cur!0
// DoubleAdd(delta,cur)
// @lvMax=delta!0-1
//]
//or[ DoubleSub(delta,half)
// @lvMax=cur!0+((cur!1 ls 0)?1,0)
// DoubleAdd(delta,cur)
// @lvMin=delta!0+1
//]
//]
//Ramshaw this time: I think that JTM was just confused. Bob Sproull
// had valid goals, but I am going to vote for purity over everything:
// the following code treats rectangles just like rectinlinear objects,
// computing the edge coordinates precisely and then ceiling’ing them to
// an integral number of pixels. This has some bad effects: two rules
// with the same mica width may not turn out to be the same number of
// pixels. But the good effects outweigh the bad: no holes or double
// coverage.
//Ramshaw one more time: Well, lets be less hard-core about our purity,
// and give people the choice. Test the LocalFidelity switch, and do the
// right thing depending.
[
test LocalFidelity ifso //try for local fidelity by Sproull’s rules
[
let half= table [ 0; #100000 ]
DoubleAdd(delta, half)
let d=delta!0//take floor
test d ge 0 then
[
@lvMin=cur!0+1
@lvMax=cur!0+d
]
or[
@lvMax=cur!0
@lvMin=cur!0+d+1
]
]
ifnot //global fidelity above everything: round each edge independently
[
test delta!0 ge 0 then
[
@lvMin=cur!0+1
DoubleAdd(delta, cur)
@lvMax=delta!0
]
or[
@lvMax=cur!0
DoubleAdd(delta,cur)
@lvMin=delta!0+1
]
]
if @lvMax ls @lvMin then @lvMax=@lvMin //guarantee that size=0 doesn’t
// happen, since none of the code is ready to handle it! (Ramshaw)
]
if CoordsInvalid then CoordsUpdate()
let dS=vec 1; let dB=vec 1
let b,bt,s,st=nil,nil,nil,nil
CoordsConvert(w, h, dS, dB, false)//Get scan,bit version
GetMinMax(CurBCoord,dB,lv b,lv bt)
GetMinMax(CurSCoord,dS,lv s,lv st)
@lvSmin=s; @lvSmax=st; @lvBmin=b; @lvBmax=bt
resultis CoordsBound(s,st,b,bt)
]
// Check four coordinates against legal limits for page, and
// expand bounding box for this page if necessary. Note that
// the tests for "bit" are done first, and only if they are satisfied
// is the "scan" direction enlarged. Thus CurSMin > CurSMax is
// a fine indication that the page is empty!
and CoordsBound(sl,sr,bl,br) = valof
[
if bl ls CurBMin then
[
if bl ls 0 then resultis false
CurBMin=bl
]
if br gr CurBMax then
[
if (br-FA*16) ge nBitsPerScan then resultis false
CurBMax=br
]
if sl ls CurSMin then
[
if sl ls 0 then resultis false
CurSMin=sl
]
if sr gr CurSMax then
[
if sr ge nScanLines then resultis false
CurSMax=sr
]
resultis true
]