-- CGBandImageImpl.mesa Cedar 3.4 -- Last changed by Ken Pier, October 8, 1982 4:06 pm DIRECTORY CGBandImage, CGBandStream USING [BandPrefix, BandIndex, BandStreamHandle, OpenBand, StartGetBand, GetBand, CloseBand ], CGBandDevice USING [BandDevice, Band, BandRef, BandsRep, GetBParameters], CGBandFormat USING [Bit, Marker, TBrickRef, tBrickSize, Run, runSize, Type, HeraldRef, HeraldObject, heraldObjectSize, ObjectRef, Object, objectSize], CGArea USING [Ref], GraphicsBasic USING [Box], CGDevice USING [Ref, Rep], CGMatrix USING [Ref, New], CGSource USING [Rep, Ref], CGStorage USING [qZone, pZone], Inline USING [LongNumber, LowHalf, HighHalf, LongMult, DIVMOD], UnsafeStorage USING [GetSystemUZone], Real USING [FixC ], Environment USING [bitsPerWord], UserTerminal USING [CursorArray, SetCursorPattern, GetCursorPattern], BitBlt USING [BBTableSpace, BBptr, AlignedBBTable, BITBLT], CGBrickBLT USING [InitializeBrickBLT, BrickBLT], --PressNetDefs USING [Printer, PageAttributes, PressSendError, PrinterReady, -- SendPageAttributes, SendLine, CloseConnection, ScanDirection], Space USING [Create, Delete, PageCount, wordsPerPage, CreateUniformSwapUnits, Handle, nullHandle, virtualMemory, Map, LongPointer], GraphicsOps USING [BitmapRef, NewBitmap, DrawBitmap], TJaMGraphics USING [Painter], Graphics USING [Context, SetCP, GetBounds, Box], Runtime USING [CallDebugger]; CGBandImageImpl: PROGRAM IMPORTS CGBandStream, CGBandDevice, CGMatrix, Real, --PressNetDefs,-- Runtime, CGStorage, Space, Inline, CGBrickBLT, BitBlt, UnsafeStorage, GraphicsOps, TJaMGraphics, UserTerminal, Graphics EXPORTS CGBandImage = { OPEN GB: GraphicsBasic, BDevice: CGBandDevice, Device: CGDevice, Area: CGArea, Matrix: CGMatrix, Source: CGSource, BStream: CGBandStream, BFormat: CGBandFormat, BB: BitBlt, Real; dataZone: ZONE = CGStorage.qZone; repZone: ZONE = CGStorage.qZone; pZone: ZONE = CGStorage.pZone; uZone: UNCOUNTED ZONE _ UnsafeStorage.GetSystemUZone[]; NoPrinter: PUBLIC BOOLEAN _ TRUE; bitsPerWord: CARDINAL = Environment.bitsPerWord; Error: SIGNAL = CODE; myMark: BFormat.Marker;-- = 98765432 Data: TYPE = REF DataRep; DataRep: TYPE = RECORD [ device: BDevice.BandDevice, bitmap: GraphicsOps.BitmapRef _ NIL, --filled in if screen base: LONG POINTER _ NIL, -- base address of band buffer or screen bitmap pages: Space.PageCount _0, -- pages in band buffer if not screen rast: CARDINAL _0, -- bitmap words per line lines: CARDINAL _0, -- bitmap lines matrix: Matrix.Ref _ NIL, bands: BDevice.BandRef _ NIL ]; New: PUBLIC PROC [bDevice: BDevice.BandDevice] RETURNS [Device.Ref] = { data: Data _ dataZone.NEW[DataRep _ [device: bDevice, bitmap: NIL, base: NIL, pages: 0, rast: 0, lines: 0, matrix: NIL, bands: NIL]]; data.bands _ InitBands[bDevice];--verifies device type match [base: , raster: data.rast, height: data.lines] _ BDevice.GetBParameters[bDevice];--returns base=NIL AcquireBandBitmap[data]; -- fill in bitmap, base, pages data.matrix _ Matrix.New[];--Matrix.New returns a new identity matrix RETURN[repZone.NEW[Device.Rep _ [ GetMatrix: GetMatrix, GetBounds: GetBounds, Show: Show, GetRaster: GetRaster, data: data]]]; }; InitBands: PROC [bDevice: BDevice.BandDevice] RETURNS [bRef: BDevice.BandRef] = { bandWidth, bandCount, bandHeight: CARDINAL _ 0; wordsRead: LONG INTEGER _ 0; bandName: STRING = "PBAND."L; stream0: BStream.BandStreamHandle _ BStream.OpenBand[bandName, 0, read]; BStream.StartGetBand[stream0]; wordsRead _ BStream.GetBand[stream0, heraldRef, BFormat.heraldObjectSize]; IF wordsRead#BFormat.heraldObjectSize OR heraldRef.mark#myMark THEN Runtime.CallDebugger["HeraldErr"L]; IF heraldRef.device#bDevice THEN Runtime.CallDebugger["HeraldDevMismatch"L]; bandWidth _ heraldRef.bandWidth; bandCount _ heraldRef.bandCount; bandHeight _ heraldRef.bandHeight; bRef _ repZone.NEW[BDevice.BandsRep[bandCount] _ [count: bandCount, height: bandHeight, width: bandWidth, list: ]]; bRef[0] _ [ymin: 0.0, ymax: bandHeight, stream: stream0]; FOR i: CARDINAL IN [1..bandCount) DO bRef[i] _ [ymin: i*bandHeight, ymax: (i+1)*bandHeight, stream: BStream.OpenBand[ bandName, i, read]]; ENDLOOP; runsRef _ uZone.NEW[RunsBuffer[bandHeight]]; lineRef _ uZone.NEW[Scanline[maxLx2+(bandWidth*bitsPerWord)]];--brickline limit is (L+xmax+L-1) bits };--InitBands AcquireBandBitmap: PROC[data: Data] = { -- updates data directly SELECT data.device FROM IN [hornet..end] => { -- get a space for band buffer longrast, longtemp, longDiv: LONG CARDINAL;--KLUDGE longDiv _ data.bands.count*Space.wordsPerPage; longrast _ data.rast; longtemp _ ((longrast*data.lines)+longDiv-1)/longDiv; -- number of words per image (+normalize for arithmetic)/(256 words per diskpage * # bands per image) IF Inline.HighHalf[longtemp]#0 THEN ERROR; data.pages _ Inline.LowHalf[longtemp]; newSpace _ Space.Create[size: data.pages, parent: Space.virtualMemory]; Space.CreateUniformSwapUnits[size: 63, parent: newSpace]; Space.Map[newSpace];--use default window data.base _ Space.LongPointer[newSpace]; }; screen => { -- get a bitmap from CedarGraphics data.bitmap _ GraphicsOps.NewBitmap[width: data.rast*bitsPerWord, height: data.lines]; data.base _ LOOPHOLE[data.bitmap.base]; }; ENDCASE => ERROR; };--AcquireBandBitmap SpaceZero: PROC[base: LONG POINTER, pages: Space.PageCount _ 0] = { a: LONG POINTER TO ARRAY [0..Space.wordsPerPage) OF WORD _ LOOPHOLE[base]; FOR i: CARDINAL IN [0..pages) DO a^ _ ALL[0]; a _ a+Space.wordsPerPage; ENDLOOP; };--SpaceZero GetMatrix: SAFE PROC[self: Device.Ref] RETURNS[Matrix.Ref] = CHECKED { data: Data _ NARROW[self.data]; RETURN[data.matrix]; }; GetBounds: SAFE PROC[self: Device.Ref] RETURNS[GB.Box] = TRUSTED { data: Data _ NARROW[self.data]; w: CARDINAL _ 16*data.rast; h: CARDINAL _ data.lines; e: REAL = 0.1; -- small fudge factor RETURN[[xmin: e, ymin: e, xmax: w-e, ymax: h-e]]; }; GetRaster: SAFE PROC [self: Device.Ref] RETURNS[LONG POINTER,CARDINAL] = TRUSTED { data: Data _ NARROW[self.data]; RETURN[data.base,data.rast]; }; Show: SAFE PROC[self: Device.Ref, area: Area.Ref, src: Source.Ref, map: CGMatrix.Ref] = TRUSTED { -- data: Data _ NARROW[self.data]; -- UNTIL Area.Empty[area] DO -- trap: Trap _ Area.Remove[area]; -- ShowTrap[data,trap,trapType,src,myBand]; -- ENDLOOP; Runtime.CallDebugger["Can't Show"L]; }; Close: PUBLIC PROC[self: Device.Ref] = { data: Data _ NARROW[self.data]; bands: BDevice.BandRef _ data.bands; bcount: CARDINAL _ bands.count; FOR bI: BStream.BandIndex IN [0..bcount) DO BStream.CloseBand[bands[bI].stream]; ENDLOOP; KillSpace[]; };--Close KillSpace: PUBLIC PROC[] = { IF newSpace#Space.nullHandle THEN {Space.Delete[newSpace]; newSpace_Space.nullHandle;}; };--KillSpace -- Modd copied from CGBrickImpl Modd: PROC[x, y: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE { RETURN [IF x >= 0 THEN (x MOD y) ELSE ((y-1) + (x+1) MOD y)]; };--Modd GetNextOb: PROC [band: BDevice.Band, index: BStream.BandIndex, objRef: BFormat.ObjectRef] = { --GetNextOb takes a band and a buffer and puts an object head for this band --into the buffer, then fills in globals RunsBuffer, tBrick buffer, as appropriate. OPEN BFormat; runWords, brickWords: CARDINAL _ 0; wordsRead: LONG INTEGER; wordsRead _ BStream.GetBand[band: band.stream, to: objRef, words: objectSize]; IF wordsRead#objectSize THEN Runtime.CallDebugger["GetObReadErr"L]; IF objRef.mark#myMark THEN Runtime.CallDebugger["GetObMarkErr"L]; IF objRef.flags.type=end THEN RETURN; IF objRef.band#index THEN ERROR; runWords _ IF objRef.flags.rect THEN BFormat.runSize ELSE BFormat.runSize*objRef.height; wordsRead _ BStream.GetBand[band: band.stream, to: runsRef, words: runWords];--read runs SELECT objRef.flags.type FROM all0, all1, bits => RETURN; --no data for allx, scanlines later for bits brick => { --tBrick and runs. Read brick. wordsRead _ BStream.GetBand[band: band.stream, to: tBrick, words: tBrickSize]; IF wordsRead # tBrickSize THEN Runtime.CallDebugger["InitBrickReadErr"L];--ran off end of stream ?! brickWords _ (tBrick.brickSize + bitsPerWord-1)/bitsPerWord; wordsRead _ BStream.GetBand[band: band.stream, to: brickBuffer, words: brickWords]; IF wordsRead # brickWords THEN Runtime.CallDebugger["InitBrickReadErr"L];--ran off end of stream ?! IF maxL ERROR; ENDCASE => ERROR; };--GetNextOb ShowBands: PUBLIC PROC [bDevice: BDevice.BandDevice] = { data: Data; bands: BDevice.BandRef; bandCount, bandWidth, bandHeight: CARDINAL; goAhead: BOOLEAN _ FALSE; dev: Device.Ref _ New[bDevice];--opens up band files data _ NARROW[dev.data];--data should contain the band device parameters !! bands _ data.bands; bandCount _ bands.count; bandWidth _ bands.width; bandHeight _ bands.height; yOffset _ 0;--assumes screen bitmap -- BEGIN OPEN PressNetDefs; -- ENABLE PressSendError => { Runtime.CallDebugger["PressNetErr"L]; GOTO exit; }; -- pageAttributes: PageAttributes; -- printer: Printer; -- scanDirection: ScanDirection; -- -- SELECT bDevice FROM -- IN [hornet..end) => { -- SELECT bDevice FROM -- hornet => {printer _ Stinger; scanDirection _ landscape}; -- platemaker => {printer _ DLP1; scanDirection _ portrait}; -- ENDCASE => ERROR; -- IF ~NoPrinter THEN { -- IF ~(goAhead _ PrinterReady[printer, 2]) -- THEN { Runtime.CallDebugger["PrinterNoGo"L]; GOTO exit;}; -- pageAttributes _ [scanDirection: scanDirection, filler: , firstScan: 0D, lastScan: data.lines-1, margin: 0, bitWc: data.rast]; -- SendPageAttributes[@pageAttributes]; -- }; -- }; -- screen => NULL; -- ENDCASE => ERROR; -- EXITS -- exit => { Close[dev]; -- IF goAhead THEN {PressNetDefs.CloseConnection[]; goAhead _ FALSE}; -- RETURN; -- };----exit -- END; FOR bI: BStream.BandIndex IN [0..bandCount) DO myBand: BDevice.Band _ bands[bI]; IF bI#0 THEN BStream.StartGetBand[myBand.stream]; -- init the stream buffer for the new band SELECT bDevice FROM IN [hornet..end) => { yOffset _ Real.FixC[myBand.ymin];-- band relative baseline SpaceZero[data.base, data.pages]; }; screen => NULL; ENDCASE => ERROR; CursorCode[bI]; --GetNextOb takes a band and a buffer and puts an object head for this band --into the buffer, then fills in globals RunsBuffer, tBrick buffer, Scanline -- as appropriate. DO GetNextOb[band: myBand, index: bI, objRef: objectRef]; SELECT objectRef.flags.type FROM all0, all1, bits, brick => Render[data: data, stream: myBand.stream]; herald => ERROR; end => EXIT; ENDCASE=> ERROR; ENDLOOP;--for each trap UNTIL end of band -- IF ~NoPrinter AND goAhead THEN {----sending to printer -- OPEN PressNetDefs; -- ENABLE PressSendError => { Runtime.CallDebugger["PressNetErr"L]; GOTO exit; }; -- tp: LONG POINTER _ data.base; -- SetFTPBoxes[]; -- FOR i: CARDINAL IN [0..bandHeight) DO -- PressNetDefs.SendLine[tp]; -- tp _ tp + data.rast; -- [] _ InvertCursor[]; -- ENDLOOP; -- EXITS -- exit => { Close[dev];----clean up open bands -- IF goAhead THEN {PressNetDefs.CloseConnection[]; goAhead _ FALSE}; -- RETURN; -- };----exit -- };----sending to printer ENDLOOP;--for each band in [0..bandcount) IF data.device = screen THEN { Paint: PROC[c: Graphics.Context] = { b: Graphics.Box _ Graphics.GetBounds[c]; Graphics.SetCP[c, b.xmin, b.ymin]; GraphicsOps.DrawBitmap[self: c, bitmap: data.bitmap, w: data.bitmap.width, -- width and height of rectangle h: data.bitmap.height, xorigin: 0, yorigin: data.bitmap.height] }; TJaMGraphics.Painter[Paint]; }; Close[dev];--close all the band streams -- IF ~NoPrinter AND goAhead THEN {PressNetDefs.CloseConnection[]; goAhead _ FALSE}; };--ShowBands Render: PROC [data: Data, stream: BStream.BandStreamHandle] = { debug: BOOLEAN = TRUE; Assert: PROC[pred: BOOLEAN] = INLINE { IF debug AND NOT pred THEN ERROR }; rect: BOOLEAN _ objectRef.flags.rect; type: BFormat.Type _ objectRef.flags.type; y,height,bandWidth: CARDINAL; line: LONG POINTER TO WORD; run: LONG POINTER TO BFormat.Run; runBump: CARDINAL; y _ objectRef.yStart-yOffset; --Assert[y<=heraldRef.bandHeight]; height _ objectRef.height; --Assert[height<=(heraldRef.bandHeight-y)]; bandWidth _ heraldRef.bandWidth; -- pointer to beginning of first destination scan line line _ data.base + Inline.LongMult[y, data.rast]; run _ LOOPHOLE[runsRef]; -- pointer to first Run -- rectangles use the same Run for each line -- trapezoids have a new Run for each line runBump _ IF rect THEN 0 ELSE BFormat.runSize; SELECT type FROM all0, all1 => { -- constant white or black bbPointer.dstBpl _ bandWidth*bitsPerWord; bbPointer.srcDesc.gray _ [reserved: , yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]; bbPointer.src.word _ IF type = all1 THEN blackWord ELSE whiteWord; bbPointer.src.bit _ 0; bbPointer.flags.gray _ TRUE; IF rect THEN {--BLT a rectangle in one shot!! bbPointer.dst.word _ line + (run.xmin/bitsPerWord); bbPointer.dst.bit _ run.xmin MOD bitsPerWord; bbPointer.width _ run.xmax-run.xmin;--may be zero !! bbPointer.height _ height;--may be zero !! BB.BITBLT[bbPointer]; }--BLT a rectangle in one shot!! ELSE {--BLT a trapezoid one line at a time bbPointer.height _ 1; THROUGH [0..height) DO bbPointer.dst.word _ line+(run.xmin/bitsPerWord); bbPointer.dst.bit _ run.xmin MOD bitsPerWord; bbPointer.width _ run.xmax-run.xmin;--may be zero !! BB.BITBLT[bbPointer]; run _ run + runBump; line _ line+bandWidth;--point to start of next line ENDLOOP; };--BLT a trapezoid one line at a time }; -- constant white or black bits => { -- specified bits (opaque), fetch each line and BLT it bbPointer.dstBpl _ bandWidth*bitsPerWord; bbPointer.srcDesc.srcBpl _ 0; -- don't care bbPointer.src.word _ LOOPHOLE[lineRef];--source address bbPointer.src.bit _ 0; bbPointer.flags.gray _ FALSE; bbPointer.height _ 1; THROUGH [0..height) DO--BLT either trap or rect one line at a time [] _ BStream.GetBand[stream, lineRef, (run.xmax-run.xmin+15)/bitsPerWord];--may fetch zero words bbPointer.dst.word _ line+(run.xmin/bitsPerWord); bbPointer.dst.bit _ run.xmin MOD bitsPerWord; bbPointer.width _ run.xmax-run.xmin;--may be zero !! BB.BITBLT[bbPointer]; run _ run + runBump; line _ line+bandWidth;--point to start of next line ENDLOOP; };-- specified bits (opaque), fetch each line and BLT it brick => { -- construct each line and BLT it L, p, hx, hy: CARDINAL; D: INTEGER _ 0; bbPointer.flags.gray _ FALSE; bbPointer.height _ 1; L _ tBrick.L; p _ tBrick.p; D _ tBrick.D; y _ objectRef.yStart; -- absolute, not relative y THROUGH [0..height) DO hy _ y MOD p; hx _ Inline.LowHalf[Modd[(run.xmin - LONG[D]*(y/p)), L]]; CGBrickBLT.BrickBLT[bbptr: bbPointer, tBrick: tBrick, destLine: line, hx: hx, hy: hy, xmin: run.xmin, xmax: run.xmax, lineBuffer: lineRef]; run _ run + runBump; y _ y+1;--point to start of next line absolute line _ line+bandWidth;--point to start of next line in band buffer ENDLOOP; };-- construct each line and BLT it ENDCASE => ERROR; };--Render CursorCode: PROC [code: CARDINAL] = { digit, digitOffset, num: CARDINAL; IF code > 99 THEN ERROR; cursorPtr^ _ boxCursorArray; wordOffset _ cursorPtr+2; num _ code; FOR j: CARDINAL IN [0..2) DO FOR i: CARDINAL DECREASING IN [0..1] DO --two digits only [num, digit] _ Inline.DIVMOD[num, 10]; digitOffset _ digit*5; cbbPtr^ _ [ dst: [word: wordOffset, bit: 5*i+3], dstBpl: 16, src: [word: @digitFont + digitOffset/16, bit: digitOffset MOD 16], srcDesc: [srcBpl[64]], width: 5, height: 6, flags: [disjoint: TRUE, dstFunc: or]]; BitBlt.BITBLT[cbbPtr]; ENDLOOP; wordOffset _ cursorPtr+9; num _ heraldRef.bandCount-1; ENDLOOP; UserTerminal.SetCursorPattern[cursorPtr^]; };--CursorCode SetFTPBoxes: PROC [] = { UserTerminal.SetCursorPattern[ftpCursorArray]; };--SetFTPBoxes InvertCursor: PROC [] = { cursorPtr^ _ UserTerminal.GetCursorPattern[]; cbbPtr^ _ [ dst: [word: cursorPtr, bit: 0], dstBpl: 16, src: [word: @blackArray, bit: 0], srcDesc: [srcBpl[16]], width: 16, height: 1, flags: [disjoint: TRUE, dstFunc: xor]]; BitBlt.BITBLT[cbbPtr]; UserTerminal.SetCursorPattern[cursorPtr^]; };--InvertCursor --START CODE FOR CGBandImageImpl --global space handle for freeing space with Close newSpace: Space.Handle _ Space.nullHandle; yOffset: CARDINAL _ 0;--communicates between ShowTrap and ShowBands -- global buffering requiring REFs heraldRef: BFormat.HeraldRef _ uZone.NEW[BFormat.HeraldObject]; heraldSizeX2: CARDINAL _ 2*BFormat.heraldObjectSize; objectRef: BFormat.ObjectRef _ uZone.NEW[BFormat.Object[1]]; TBrickBuffer: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF WORD];--buffer for tBrick words coming from stream maxTBrick: CARDINAL = 400+BFormat.tBrickSize;--6400 bits maximum in a tBrick for now brickBuffer: LONG POINTER TO TBrickBuffer _ uZone.NEW[TBrickBuffer[maxTBrick]]; RunsBuffer: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF BFormat.Run]; RunsRef: TYPE = LONG POINTER TO RunsBuffer; runsRef: RunsRef _ NIL; -- initialized by InitBands runsIndex: CARDINAL _ 0; -- runs buffer index maxL: CARDINAL = 2000;--longest permitted L for a thresholded brick maxLx2: CARDINAL = maxL*2;--longest permitted L for a thresholded brick times 2 Scanline: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF BOOLEAN]; LineRef: TYPE = LONG POINTER TO Scanline; lineRef: LineRef _ NIL; -- initialized by InitBands lineIndex: CARDINAL _ 0;-- Scanline bit index BBTable: BB.BBTableSpace; bbPointer: BB.BBptr _ CGBrickBLT.InitializeBrickBLT[@BBTable]; --globals for CursorCode cbbTable: BB.BBTableSpace; cbbPtr: BB.BBptr _ BB.AlignedBBTable[@cbbTable]; digitFont: ARRAY [0..24) OF CARDINAL _ [ -- Strike-format font, densely packed, characters 6 high, 5 wide 30614B, 61474B, 167461B, 117000B, 45222B, 112441B, 512B, 57000B, 54202B, 22471B, 141062B, 57000B, 64214B, 14405B, 21111B, 157000B, 44220B, 117645B, 22110B, 57000B, 31736B, 60430B, 142063B, 117000B]; boxCursorArray: UserTerminal.CursorArray _ [ -- one bit wide border rectangle 177777B, 100001B, 100001B, 100001B, 100001B, 100001B, 100001B, 100001B, 177777B, 100001B, 100001B, 100001B, 100001B, 100001B, 100001B, 177777B]; ftpCursorArray: UserTerminal.CursorArray _ [ -- ftp boxes 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 377B, 377B, 377B, 377B, 377B, 377B, 377B, 377B]; blackArray: UserTerminal.CursorArray _ ALL[177777B]; wordOffset: LONG POINTER TO UserTerminal.CursorArray _ NIL; cursorPtr: LONG POINTER TO UserTerminal.CursorArray _ uZone.NEW[UserTerminal.CursorArray]; blackWord: LONG POINTER TO CARDINAL _ uZone.NEW[CARDINAL _ 177777B]; whiteWord: LONG POINTER TO CARDINAL _ uZone.NEW[CARDINAL _ 0]; --fix up tBrick from brickBuffer tBrick: BFormat.TBrickRef _ LOOPHOLE[brickBuffer];--common storage covering tBHead+brickBuffer brickBuffer _ brickBuffer+BFormat.tBrickSize; }. --CGBandImageImpl LOG --created 12-Nov-81 12:59:29, Pier --large change to Cedar 2.2 and new CG, 21-Dec-81 -- large rework to include band devices like Hornet, 5-Jan-82 -- replaced NEW with pZone.NEW --added noPrinter, fixed bug in PrinterNoGo failing to Close[dev], 14-Jan-82 --added Herald stuff, fixed all divisions(/) to roundoff correctly 15-Jan-82 -- formatted consistent with other CG modules, 19-Jan-82 --fixed potential bug in xline_ using LongMult, 21-JAN-82 --fixed initialization of scanning to account for empty traps in bandfile 27-JAN-82 --changed to global NEWs and buffering, 28-JAN-82 --added single file stuff, 2/2/82 --added scan line buffer, 2/3/82 --added platemaker device, swap units 2/5/82 --added CursorCode, 2/10/82 --complete rewrite for new band format, 2/23/82 --removed run.count field, skipcount capability, 3/14/82 --changed buffers from collected to uncollected zones, 3/30/82 --removed single file stuff, 3/30/82 --swapped UnsafeStorage for Heap; changed to allow screen -- bitmaps for Viewers, May 3, 1982 --Cedar 3.2, July 13, 1982