-- CGAISImageImpl.mesa -- Last changed by Doug Wyatt, October 4, 1982 11:35 am -- Last changed by Paul Rovner, August 10, 1983 11:34 am DIRECTORY Basics USING [LongDiv, LongMult], CGAIS USING [APH, Header, password, RasterPart, UCA, UCACodingType], CGArea USING [Empty, Ref], CGContext USING [GenBox, Rep], CGDevice USING [Ref], CGMatrix USING [Assign, Concat, ConcatTransformation, Make, New, Ref, SetTrans], CGPrivate USING [Context], CGSource USING [Ref, Rep], CGStorage USING [qZone], GraphicsOps USING [], -- exports only GraphicsBasic USING [black], FS USING [Open, OpenFile, Read], PrincOps USING [wordsPerPage], Rope USING [ROPE], VM USING [Interval, Free, Allocate, AddressForPageNumber]; CGAISImageImpl: CEDAR MONITOR IMPORTS Basics, CGArea, CGContext, CGMatrix, CGStorage, FS, VM EXPORTS CGPrivate, GraphicsOps, GraphicsBasic = { OPEN AIS: CGAIS; Context: TYPE = CGPrivate.Context; ContextData: TYPE = REF ContextDataRep; ContextDataRep: PUBLIC TYPE = CGContext.Rep; ImageObject: PUBLIC TYPE = DataRep; -- export to GraphicsBasic -- This is cheating! -- A new representation-independent Image interface is neeeded. -- But for now, this module exports NewAisImage and ImageBox -- directly into GraphicsOps. Data: TYPE = REF DataRep; DataRep: TYPE = RECORD [ file: FS.OpenFile, -- the AIS file rasterBase: INT, -- where the raster begins rasterPages: INT, -- number of pages in the raster scanDirection: CARDINAL, -- scan direction code scanCount: CARDINAL, -- number of scan lines in raster scanLength: CARDINAL, -- pixels per scan line wordsPerLine: CARDINAL, -- words per scan line miv: CGMatrix.Ref, -- image-to-virtual matrix matrix: CGMatrix.Ref, -- image-to-device matrix src: CGSource.Ref -- source info for image ]; dataZone: ZONE = CGStorage.qZone; srcZone: ZONE = CGStorage.qZone; repZone: ZONE = CGStorage.qZone; MalformedFile: ERROR = CODE; NotImplemented: ERROR = CODE; -- Monitored data: cache most recently mapped space cachedFile: FS.OpenFile _ [NIL]; cachedSpace: VM.Interval _ [0, 0]; -- end monitored data NewAisImage: PUBLIC PROC[name: Rope.ROPE] RETURNS[Data] = { RETURN[NewAisImageFromCapability[FS.Open[name]]]; }; NewAisImageFromCapability: PUBLIC PROC[file: FS.OpenFile] RETURNS[Data] = TRUSTED { lp: LONG POINTER; header: LONG POINTER TO AIS.Header _ NIL; part: LONG POINTER TO AIS.APH _ NIL; rp: LONG POINTER TO AIS.RasterPart _ NIL; up: LONG POINTER TO AIS.UCA _ NIL; lines,pixels,wpl,bps: CARDINAL; rbase, apages, rpages: CARDINAL; rwords: LONG CARDINAL; self: Data _ NIL; space: VM.Interval _ VM.Allocate[count: 4]; -- for reading header { ENABLE UNWIND => VM.Free[space]; header _ lp _ VM.AddressForPageNumber[space.page]; FS.Read[file: file, from: 0, nPages: 4, to: lp]; IF header.password=AIS.password THEN part _ LOOPHOLE[header+SIZE[AIS.Header]] ELSE ERROR MalformedFile; -- Wrong password apages _ header.attributeLength/PrincOps.wordsPerPage; rbase _ 1+apages; IF part.type=raster THEN { rp _ LOOPHOLE[part]; part _ part+part.length } ELSE ERROR MalformedFile; -- Raster part missing IF rp.samplesPerPixel=1 THEN NULL ELSE ERROR NotImplemented; -- Can't handle multiple samples per pixel IF rp.codingType=AIS.UCACodingType THEN up _ LOOPHOLE[rp+SIZE[AIS.RasterPart]] ELSE ERROR NotImplemented; -- Coding type not UCA IF up.scanLinesPerBlock=LAST[CARDINAL] THEN NULL ELSE ERROR NotImplemented; -- Can't handle blocked encoding yet lines _ rp.scanCount; pixels _ rp.scanLength; wpl _ up.wordsPerScanLine; bps _ up.bitsPerSample; rwords _ Basics.LongMult[lines, wpl]; -- number of words in raster part rpages _ Basics.LongDiv[rwords+(PrincOps.wordsPerPage-1), PrincOps.wordsPerPage]; -- number of pages needed self _ dataZone.NEW[DataRep _ [file: file, rasterBase: rbase, rasterPages: rpages, scanDirection: rp.scanDirection, scanCount: lines, scanLength: pixels, wordsPerLine: wpl, miv: NIL, matrix: NIL, src: NIL]]; SELECT self.scanDirection FROM 2 => self.miv _ CGMatrix.Make[[1,0,0,1,0,0]]; 3 => self.miv _ CGMatrix.Make[[1,0,0,-1,0,lines]]; 8 => self.miv _ CGMatrix.Make[[0,1,1,0,0,0]]; ENDCASE => ERROR; -- unexpected scanDirection self.matrix _ CGMatrix.New[]; -- placeholder for image-to-device mapping self.src _ srcZone.NEW[CGSource.Rep _ [type: array, mode: opaque, fat: FALSE, bps: bps, color: GraphicsBasic.black, xbase: NIL, xrast: wpl, Get: NIL]]; }; VM.Free[space]; RETURN[self]; }; DrawImage: PUBLIC PROC[self: Context, image: Data, raw: BOOLEAN] = { ctx: ContextData _ NARROW[self.data]; data: Data _ image; T: CGMatrix.Ref _ ctx.matrix; -- virtual to destination mapping M: CGMatrix.Ref _ data.matrix; -- image to destination mapping src: CGSource.Ref _ data.src; wpl: CARDINAL _ data.wordsPerLine; device: CGDevice.Ref _ ctx.device; DrawImageEntry: ENTRY PROC = TRUSTED { ENABLE UNWIND => cachedFile _ [NIL]; -- and release lock file: FS.OpenFile _ data.file; base: INT _ data.rasterBase; pages: INT _ data.rasterPages; space: VM.Interval _ cachedSpace; area: CGArea.Ref; IF cachedFile#file THEN { create: BOOLEAN _ FALSE; -- first ensure a large enough space IF space=[0, 0] THEN create _ TRUE ELSE IF space.count RETURN[0,0,w,h]; 8 => RETURN[0,0,h,w]; ENDCASE => ERROR; -- Unexpected scanDirection; }; }.