<> <> <> <> DIRECTORY AIS USING [Buffer, CloseFile, CloseWindow, FRef, OpenFile, OpenWindow, Raster, ReadRaster, ScanMode, UnsafeReadLine, WRef], Basics USING [BITSHIFT, bitsPerWord, LongMult], FS USING [Error, GetName, OpenFile], ImagerPixelArrays USING [MaxSampleValues, MaxSampleValuesRep, PixelArray, PixelArrayRep], ImagerPixelMaps USING [BoundedWindow, DeviceRectangle, Intersect, PixelMap, PixelMapRep, Reshape, Transfer, Window], ImagerTransform USING [Concat, Create, Rotate, Transformation, Translate], Process USING [Detach, GetCurrent, InitializeCondition, MsecToTicks, Pause], Rope USING [Equal, Find, Replace, ROPE], SafeStorage USING [ReclaimCollectibleObjects]; ImagerPixelArraysImpl: CEDAR MONITOR IMPORTS AIS, Basics, FS, ImagerPixelMaps, ImagerTransform, Process, Rope, SafeStorage EXPORTS ImagerPixelArrays ~ BEGIN ROPE: TYPE ~ Rope.ROPE; PixelMap: TYPE ~ ImagerPixelMaps.PixelMap; DeviceRectangle: TYPE ~ ImagerPixelMaps.DeviceRectangle; Transformation: TYPE ~ ImagerTransform.Transformation; MaxSampleValues: TYPE ~ ImagerPixelArrays.MaxSampleValues; MaxSampleValuesRep: TYPE ~ ImagerPixelArrays.MaxSampleValuesRep; PixelArray: TYPE ~ ImagerPixelArrays.PixelArray; PixelArrayRep: TYPE ~ ImagerPixelArrays.PixelArrayRep; BufferStatus: TYPE ~ {empty, filling, full}; AISData: TYPE ~ REF AISDataRep; AISDataRep: TYPE ~ RECORD [ link: REF AISDataRep, <> fullFName: ROPE, <> lifetime: NAT _ 0, raster: AIS.Raster, lgBitsPerPixel: NAT, ais: AIS.FRef, window: AIS.WRef, readsInProgress: NAT _ 0, bufferUseCount: NAT _ 0, buffer: PixelMap _ [0,0,0,0,0,0,NIL], lineMapStorage: REF ImagerPixelMaps.PixelMapRep _ NIL ]; ErrorCode: TYPE ~ { BadSampleSelection, SeparationsHaveInconsistentDimensions, SeparationsHaveInconsistentScanDirections, PixelArrayMustBeAtOrigin }; Error: ERROR[code: ErrorCode] ~ CODE; bufferStatusChanged: CONDITION; maxLifetime: NAT _ 24; RectangleInsidePixelMap: PROC [rectangle: DeviceRectangle, pixelMap: PixelMap] RETURNS [inside: BOOLEAN]~ { inside _ IF pixelMap.refRep = NIL THEN FALSE ELSE IF rectangle.sSize = 0 OR rectangle.fSize = 0 THEN TRUE ELSE (rectangle = ImagerPixelMaps.Intersect[rectangle, pixelMap.BoundedWindow]); }; TryBuffer: ENTRY PROC [aisData: AISData, rectangle: DeviceRectangle] RETURNS [PixelMap] ~ { ENABLE UNWIND => NULL; WHILE aisData.readsInProgress > 0 DO WAIT bufferStatusChanged ENDLOOP; aisData.lifetime _ maxLifetime; IF RectangleInsidePixelMap[rectangle, aisData.buffer] THEN { IF aisData.bufferUseCount < NAT.LAST THEN aisData.bufferUseCount _ aisData.bufferUseCount + 1; RETURN [aisData.buffer] } ELSE RETURN [[0,0,0,0,0,0,NIL]] }; FreeBuffer: ENTRY PROC [aisData: AISData, pixelMap: PixelMap] ~ { ENABLE UNWIND => NULL; IF aisData.buffer = pixelMap AND aisData.bufferUseCount > 0 AND aisData.bufferUseCount < NAT.LAST THEN { aisData.bufferUseCount _ aisData.bufferUseCount - 1; }; IF aisData.bufferUseCount = 0 THEN BROADCAST bufferStatusChanged; }; GetScratchBuffer: ENTRY PROC [aisData: AISData] RETURNS [scratch: REF ImagerPixelMaps.PixelMapRep _ NIL] ~ { ENABLE UNWIND => NULL; IF aisData.bufferUseCount = 0 THEN { scratch _ aisData.buffer.refRep; aisData.buffer _ [0,0,0,0,0,0,NIL]; }; aisData.readsInProgress _ aisData.readsInProgress + 1; }; PostFullBuffer: ENTRY PROC [aisData: AISData, buffer: PixelMap] ~ { IF aisData.readsInProgress > 0 THEN aisData.readsInProgress _ aisData.readsInProgress - 1; aisData.bufferUseCount _ 1; aisData.buffer _ buffer; IF cleanupProcess = NIL THEN TRUSTED { Process.Detach[cleanupProcess _ FORK CleanupProcess]; }; BROADCAST bufferStatusChanged; }; SetAISFile: ENTRY PROC [aisData: AISData, ais: AIS.FRef, window: AIS.WRef] ~ { IF aisData.ais = NIL THEN { aisData.ais _ ais; aisData.window _ window; }; IF cleanupProcess = NIL THEN TRUSTED { Process.Detach[cleanupProcess _ FORK CleanupProcess]; }; }; aisDataHead: AISData _ NIL; RegisterAISData: ENTRY PROC [aisDataRep: AISDataRep] RETURNS [aisData: AISData] ~ { FOR p: AISData _ aisDataHead, p.link UNTIL p=NIL DO IF aisDataRep.fullFName.Equal[p.fullFName, FALSE] THEN { IF p.ais = NIL THEN { p.ais _ aisDataRep.ais; p.window _ aisDataRep.window; }; RETURN [p]; }; ENDLOOP; aisData _ NEW[AISDataRep _ aisDataRep]; aisData.link _ aisDataHead; aisDataHead _ aisData; RETURN [aisData] }; CleanupChain: ENTRY PROC ~ { ENABLE UNWIND => NULL; somethingFreed: BOOLEAN _ FALSE; nothingToDo: BOOLEAN _ TRUE; FOR p: AISData _ aisDataHead, p.link UNTIL p=NIL DO IF p.ais # NIL OR p.buffer.refRep # NIL THEN nothingToDo _ FALSE; IF p.lifetime # 0 THEN { IF p.readsInProgress = 0 AND p.bufferUseCount = 0 THEN p.lifetime _ p.lifetime - 1 } ELSE { IF p.readsInProgress = 0 THEN { IF p.window # NIL THEN AIS.CloseWindow[p.window]; p.window _ NIL; IF p.ais # NIL THEN AIS.CloseFile[p.ais]; p.ais _ NIL; }; IF p.bufferUseCount = 0 AND p.buffer.refRep # NIL THEN { p.buffer _ [0,0,0,0,0,0,NIL]; p.lineMapStorage _ NIL; somethingFreed _ TRUE; }; }; ENDLOOP; IF somethingFreed THEN SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE]; IF nothingToDo THEN cleanupProcess _ NIL; }; cleanupProcess: PROCESS _ NIL; CleanupProcess: PROC ~ { WHILE cleanupProcess = Process.GetCurrent[] DO Process.Pause[Process.MsecToTicks[5000]]; CleanupChain[]; ENDLOOP; }; GetBuffer: PROC [aisData: AISData, rectangle: DeviceRectangle] RETURNS [pixelMap: PixelMap] ~ { <> <> pixelMap _ TryBuffer[aisData, rectangle]; UNTIL RectangleInsidePixelMap[rectangle, pixelMap] DO scratch: REF ImagerPixelMaps.PixelMapRep _ GetScratchBuffer[aisData]; TRUSTED BEGIN ENABLE UNWIND => PostFullBuffer[aisData, [0,0,0,0,0,0,NIL]]; scratchBits: LONG CARDINAL _ IF scratch = NIL THEN 0 ELSE scratch.words*Basics.bitsPerWord; lineMap: ImagerPixelMaps.PixelMap _ ImagerPixelMaps.Reshape[aisData.lineMapStorage, aisData.lgBitsPerPixel, [rectangle.sMin, 0, 1, aisData.raster.scanLength]]; lineBufferDesc: AIS.Buffer _ [length: lineMap.refRep.words, addr: lineMap.refRep.pointer]; aisData.lineMapStorage _ lineMap.refRep; IF scratchBits >= Basics.LongMult[rectangle.sSize, aisData.raster.scanLength]*Basics.BITSHIFT[1, aisData.lgBitsPerPixel] THEN { rectangle.fMin _ 0; rectangle.fSize _ aisData.raster.scanLength; }; pixelMap _ ImagerPixelMaps.Reshape[scratch, aisData.lgBitsPerPixel, rectangle]; IF aisData.ais = NIL THEN { ais: AIS.FRef _ AIS.OpenFile[aisData.fullFName]; window: AIS.WRef _ AIS.OpenWindow[ais]; SetAISFile[aisData, ais, window]; }; FOR s: INTEGER IN [rectangle.sMin..rectangle.sMin+rectangle.sSize) DO lineMap.sOrigin _ s; AIS.UnsafeReadLine[aisData.window, lineBufferDesc, s]; pixelMap.Transfer[lineMap]; ENDLOOP; END; PostFullBuffer[aisData, pixelMap]; ENDLOOP; }; AISGet: PROC [self: PixelArray, select: NAT, rectangle: DeviceRectangle] RETURNS [pixelMap: PixelMap] ~ { dataList: LIST OF AISData _ NARROW[self.data]; IF dataList = NIL THEN ERROR Error[$BadSampleSelection]; FOR i: NAT IN [0..select) DO dataList _ dataList.rest; IF dataList = NIL THEN ERROR Error[$BadSampleSelection]; ENDLOOP; pixelMap _ GetBuffer[dataList.first, rectangle]; }; AISRelease: PROC [self: PixelArray, select: NAT, pixelMap: PixelMap] ~ { dataList: LIST OF AISData _ NARROW[self.data]; IF dataList = NIL THEN ERROR Error[$BadSampleSelection]; FOR i: NAT IN [0..select) DO dataList _ dataList.rest; IF dataList = NIL THEN ERROR Error[$BadSampleSelection]; ENDLOOP; FreeBuffer[dataList.first, pixelMap]; }; CreateAISData: PROC [aisName: ROPE] RETURNS [aisData: AISData] ~ { ais: AIS.FRef _ AIS.OpenFile[aisName]; window: AIS.WRef _ AIS.OpenWindow[ais]; aisDataRep: AISDataRep; aisDataRep.fullFName _ FS.GetName[ais.file].attachedTo; IF aisDataRep.fullFName = NIL THEN aisDataRep.fullFName _ FS.GetName[ais.file].fullFName; aisDataRep.lifetime _ maxLifetime; aisDataRep.raster _ AIS.ReadRaster[ais]; aisDataRep.lgBitsPerPixel _ SELECT aisDataRep.raster.bitsPerPixel FROM 0, 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4, ENDCASE => ERROR; aisDataRep.ais _ ais; aisDataRep.window _ window; aisData _ RegisterAISData[aisDataRep]; IF aisData.ais # aisDataRep.ais THEN { AIS.CloseWindow[aisDataRep.window]; AIS.CloseFile[aisDataRep.ais]; }; }; TransformationFromScanMode: PROC [scanMode: AIS.ScanMode, lines, pixelsPerLine: INT] RETURNS [ImagerTransform.Transformation] ~ { pixelDeltaX: REAL _ SELECT scanMode FROM ru, rd => 1, lu, ld => -1, ENDCASE => 0; pixelDeltaY: REAL _ SELECT scanMode FROM ul, ur => 1, dr, dl => -1, ENDCASE => 0; lineDeltaX: REAL _ SELECT scanMode FROM dr, ur => 1, ul, dl => -1, ENDCASE => 0; lineDeltaY: REAL _ SELECT scanMode FROM ru, lu => 1, ld, rd => -1, ENDCASE => 0; t: ImagerTransform.Transformation ~ ImagerTransform.Create[ a: lineDeltaX, b: pixelDeltaX, c: MAX[-(pixelDeltaX*pixelsPerLine + lineDeltaX*lines), 0], d: lineDeltaY, e: pixelDeltaY, f: MAX[-(pixelDeltaY*pixelsPerLine + lineDeltaY*lines), 0] ]; RETURN [t] }; PixelArrayFromAIS: PUBLIC PROC [aisName: ROPE] RETURNS [pixelArray: PixelArray] ~ { aisList: LIST OF AISData _ NIL; starLoc: INT _ Rope.Find[aisName, "*"]; nsep: NAT _ 1; IF starLoc < 0 THEN aisList _ LIST[CreateAISData[aisName]] ELSE { Do: PROC [s1, s2: ROPE] ~ { aisData: AISData _ NIL; aisData _ CreateAISData[aisName.Replace[starLoc, 1, s1] ! FS.Error => CONTINUE]; IF aisData = NIL THEN aisData _ CreateAISData[aisName.Replace[starLoc, 1, s2]]; aisList _ CONS[aisData, aisList]; }; Do["Blue", "blu"]; Do["Green", "grn"]; Do["Red", "red"]; nsep _ 3; }; pixelArray _ NEW[PixelArrayRep]; pixelArray.sPixels _ aisList.first.raster.scanCount; pixelArray.fPixels _ aisList.first.raster.scanLength; pixelArray.maxSampleValue _ NEW[MaxSampleValuesRep[nsep]]; nsep _ 0; FOR p: LIST OF AISData _ aisList, p.rest UNTIL p=NIL DO IF pixelArray.sPixels # p.first.raster.scanCount THEN ERROR Error[$SeparationsHaveInconsistentDimensions]; IF pixelArray.fPixels # p.first.raster.scanLength THEN ERROR Error[$SeparationsHaveInconsistentDimensions]; IF aisList.first.raster.scanMode # p.first.raster.scanMode THEN ERROR Error[$SeparationsHaveInconsistentScanDirections]; pixelArray.maxSampleValue[nsep] _ Basics.BITSHIFT[1, Basics.BITSHIFT[1, p.first.lgBitsPerPixel]]-1; nsep _ nsep + 1; ENDLOOP; pixelArray.m _ TransformationFromScanMode[aisList.first.raster.scanMode, pixelArray.sPixels, pixelArray.fPixels]; pixelArray.aisName _ aisName; pixelArray.get _ AISGet; pixelArray.release _ AISRelease; pixelArray.data _ aisList; }; PixelMapGet: PROC [self: PixelArray, select: NAT, rectangle: DeviceRectangle] RETURNS [pixelMap: PixelMap] ~ { dataList: LIST OF ImagerPixelMaps.PixelMap _ NARROW[self.data]; IF dataList = NIL THEN ERROR Error[$BadSampleSelection]; FOR i: NAT IN [0..select) DO dataList _ dataList.rest; IF dataList = NIL THEN ERROR Error[$BadSampleSelection]; ENDLOOP; RETURN [dataList.first]; }; NullRelease: PROC [self: PixelArray, select: NAT, pixelMap: PixelMap] ~ {}; PixelArrayFromPixelMaps: PUBLIC PROC [pixelMaps: LIST OF PixelMap, m: ImagerTransform.Transformation] RETURNS [pixelArray: PixelArray] ~ { window: DeviceRectangle _ pixelMaps.first.Window; nsep: NAT _ 0; IF window.sMin # 0 OR window.fMin # 0 THEN ERROR Error[$PixelArrayMustBeAtOrigin]; FOR p: LIST OF PixelMap _ pixelMaps, p.rest UNTIL p=NIL DO IF p.first.Window # window THEN ERROR Error[$SeparationsHaveInconsistentDimensions]; nsep _ nsep + 1; ENDLOOP; pixelArray _ NEW[PixelArrayRep]; pixelArray.sPixels _ window.sSize; pixelArray.fPixels _ window.fSize; pixelArray.maxSampleValue _ NEW[MaxSampleValuesRep[nsep]]; nsep _ 0; FOR p: LIST OF PixelMap _ pixelMaps, p.rest UNTIL p=NIL DO pixelArray.maxSampleValue[nsep] _ Basics.BITSHIFT[1, Basics.BITSHIFT[1, p.first.refRep.lgBitsPerPixel]]-1; nsep _ nsep + 1; ENDLOOP; pixelArray.m _ IF m # NIL THEN m ELSE ImagerTransform.Rotate[-90].Concat[ImagerTransform.Translate[0, pixelArray.sPixels]]; pixelArray.get _ PixelMapGet; pixelArray.release _ NullRelease; pixelArray.data _ pixelMaps; }; <> <<Body>> <<};>> <<>> <> <<Body>> <<};>> <<>> <<>> TRUSTED {Process.InitializeCondition[@bufferStatusChanged, Process.MsecToTicks[1000]]}; END.