// 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: // 2 X changed -- only update corresponding bit coord // 1 Y 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 used ColorUsed //True if , used MicaX //Last MicaY //Last ResFactorS //Multiplier for S resolution ResFactorB //Multiplier for B resolution (micas=>bits) ] // File-wide structure and manifest declarations. structure EHC : //EH + some stuff [ @EH next word //Pointer to next entity ELCPos word 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 ] (635)