ImagerPixelArraysImpl.mesa
Copyright © 1984, Xerox Corporation. All rights reserved.
Michael Plass, August 2, 1984 9:35:47 am PDT
Doug Wyatt, August 10, 1984 3:47:33 pm PDT
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,
all reps linked together to allow global management
fullFName: ROPE,
at least this is present
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: BOOLEANFALSE;
nothingToDo: BOOLEANTRUE;
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: PROCESSNIL;
CleanupProcess: PROC ~ {
WHILE cleanupProcess = Process.GetCurrent[] DO
Process.Pause[Process.MsecToTicks[5000]];
CleanupChain[];
ENDLOOP;
};
GetBuffer: PROC [aisData: AISData, rectangle: DeviceRectangle] RETURNS [pixelMap: PixelMap] ~ {
Gets at least the requested area into a pixelMap
Increments the bufferUseCount
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 CARDINALIF 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: REALSELECT scanMode FROM
ru, rd => 1,
lu, ld => -1,
ENDCASE => 0;
pixelDeltaY: REALSELECT scanMode FROM
ul, ur => 1,
dr, dl => -1,
ENDCASE => 0;
lineDeltaX: REALSELECT scanMode FROM
dr, ur => 1,
ul, dl => -1,
ENDCASE => 0;
lineDeltaY: REALSELECT 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;
};
ExtractPixelArray: PUBLIC PROC [pixelArray: PixelArray, select: LIST OF NAT] RETURNS [PixelArray] ~ {
Body
};
JoinPixelArrays: PUBLIC PROC [pixelArrays: LIST OF PixelArray] RETURNS [PixelArray] ~ {
Body
};
TRUSTED {Process.InitializeCondition[@bufferStatusChanged, Process.MsecToTicks[1000]]};
END.