ImagerAISPixelArrayImpl.mesa
Copyright © 1984, Xerox Corporation. All rights reserved.
Doug Wyatt, November 5, 1984 3:07:48 pm PST
DIRECTORY
AISFileFormat USING [AttributeHeader, bytesPerWord, CommentPart, nil, PartHeader, passwordValue, PhotometryPart, PlacementPart, RasterPart, sizeAttributeHeader, sizeCommentPart, sizePartHeader, sizePhotometryPart, sizePlacementPart, sizeRasterPart, sizeUCACoding, UCACoding, wordsPerPage],
FS USING [StreamOpen],
ImagerPixelArray,
ImagerTransformation USING [Create, Transformation],
IO USING [EndOfStream, GetBlock, GetIndex, SetIndex, STREAM, UnsafeGetBlock],
Rope USING [FromProc, ROPE],
RuntimeError USING [BoundsFault];
ImagerAISPixelArrayImpl: CEDAR PROGRAM
IMPORTS FS, ImagerPixelArray, ImagerTransformation, IO, Rope, RuntimeError
EXPORTS ImagerPixelArray
~ BEGIN OPEN ImagerPixelArray;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD[
stream: STREAM, -- a stream on the AIS file
sSize, fSize: NAT ← 0, -- size in slow and fast directions
samplesPerPixel: NAT ← 0,
comments: LIST OF ROPE ← NIL,
m: ImagerTransformation.Transformation ← NIL, -- from [s, f] to [x, y]
lgBitsPerSample: NAT ← 0,
rasterIndex: INT ← 0, -- byte index of the beginning of the raster section
bytesPerLine: INT ← 0, -- bytes per scan line
linesPerBlock: NAT ← 0, -- number of scan lines per block, 0 if no blocks
bytesPadding: INT ← 0, -- bytes of padding at the end of each block
sBuffer: NAT ← 0, -- scan line currently in the buffer
buffer: REF TEXT ← NIL
];
Error: PUBLIC ERROR ~ CODE;
Assert:
PROC[assertion:
BOOL] ~ {
IF assertion
THEN
NULL
ELSE
ERROR Error };
ReadWords:
UNSAFE
PROC[stream:
STREAM, base:
LONG
POINTER, words:
INT] ~
UNCHECKED {
Note: "words" means the 16-bit words used by AISFileFormat!
bytes: INT ~ words*AISFileFormat.bytesPerWord;
IF IO.UnsafeGetBlock[stream, [base: base, count: bytes]]=bytes THEN NULL
ELSE ERROR IO.EndOfStream[stream]; -- file too short
};
SkipWords:
PROC[stream:
STREAM, words:
INT] ~ {
Note: "words" means the 16-bit words used by AISFileFormat!
bytes: INT ~ words*AISFileFormat.bytesPerWord;
IO.SetIndex[stream, IO.GetIndex[stream]+bytes];
};
Open:
PROC[name:
ROPE]
RETURNS[Data] ~ {
OPEN AISFileFormat;
stream: STREAM ~ FS.StreamOpen[name];
data: Data ~ NEW[DataRep ← [stream: stream]];
header: AttributeHeader;
raster: RasterPart;
placement: PlacementPart;
photometry: PhotometryPart;
rasterFound, placementFound, photometryFound: BOOL ← FALSE;
TRUSTED { ReadWords[stream, @header, sizeAttributeHeader] };
Assert[header.password=passwordValue]; -- check password
Assert[header.length>0 AND (header.length MOD wordsPerPage)=0];
data.rasterIndex ← header.length*bytesPerWord;
FOR firstPart:
BOOL ←
TRUE,
FALSE
WHILE
IO.GetIndex[stream]<data.rasterIndex
DO
part: PartHeader;
TRUSTED { ReadWords[stream, @part, sizePartHeader] };
SELECT part.type
FROM
nil => { Assert[part.length=0]; EXIT }; -- should be a zero word
raster => {
Assert[firstPart]; -- raster part must be first
Assert[part.length>=(sizePartHeader+sizeRasterPart)];
TRUSTED { ReadWords[stream, @raster, sizeRasterPart] };
Assert[raster.scanCount>0 AND raster.scanLength>0 AND raster.samplesPerPixel>0];
SELECT raster.codingType
FROM
uca => {
uca: UCACoding;
sizeCoding: NAT ~ part.length-(sizePartHeader+sizeRasterPart);
Assert[sizeCoding IN[sizeUCACoding-1..sizeUCACoding]];
TRUSTED { ReadWords[stream, @uca, sizeCoding] };
Assert[
INT[uca.bitsPerSample]*
INT[raster.samplesPerPixel]*
INT[raster.scanLength]
<=INT[16--bitsPerWord--]*INT[uca.wordsPerScanLine]];
data.lgBitsPerSample ←
SELECT uca.bitsPerSample
FROM
1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4,
ENDCASE => ERROR Error; -- unsupported bitsPerSample
data.bytesPerLine ← INT[uca.wordsPerScanLine]*bytesPerWord;
IF sizeCoding<sizeUCACoding THEN Assert[uca.scanLinesPerBlock=nil];
IF uca.scanLinesPerBlock#nil
THEN {
data.linesPerBlock ← uca.scanLinesPerBlock;
data.bytesPadding ← INT[uca.paddingPerBlock]*bytesPerWord;
};
};
ENDCASE => ERROR Error; -- unknown coding type
rasterFound ← TRUE;
};
placement => {
Assert[NOT placementFound]; -- at most one placement part
Assert[part.length=(sizePartHeader+sizePlacementPart)];
TRUSTED { ReadWords[stream, @placement, sizePlacementPart] };
IF placement.xLeft=-1 AND placement.yBottom=-1
AND placement.xWidth=-1 AND placement.yHeight=-1 THEN NULL
ELSE Assert[placement.xWidth>0 AND placement.yHeight>0];
placementFound ← TRUE;
};
photometry => {
Assert[NOT photometryFound]; -- at most one photometry part
Assert[part.length>=(sizePartHeader+sizePhotometryPart)];
TRUSTED { ReadWords[stream, @photometry, sizePhotometryPart] };
-- Ignore histogram, if any
SkipWords[stream, part.length-(sizePartHeader+sizePhotometryPart)];
photometryFound ← TRUE;
};
comment => {
comment: CommentPart;
length: NAT ← 0; -- number of characters in the string
rope: ROPE ← NIL;
Assert[part.length>sizePartHeader AND (part.length-sizePartHeader)<=sizeCommentPart];
TRUSTED { ReadWords[stream, @comment, part.length-sizePartHeader] };
length ← ORD[comment[0]];
Assert[(part.length-sizePartHeader)=(1+length+1)/2];
{
-- turn the comment into a ROPE
i: NAT ← 0;
p: PROC RETURNS[CHAR] ~ { RETURN[comment[i ← i+1]] };
rope ← Rope.FromProc[len: length, p: p];
};
data.comments ← CONS[rope, data.comments];
};
ENDCASE => SkipWords[stream, part.length-sizePartHeader]; -- unknown part type
ENDLOOP;
Assert[rasterFound]; -- raster part is mandatory
data.fSize ← raster.scanLength;
data.sSize ← raster.scanCount;
data.samplesPerPixel ← raster.samplesPerPixel;
SELECT raster.scanDirection
FROM
0, 8 => data.m ← ImagerTransformation.Create[1, 0, 0, 0, 1, 0];
3 => data.m ← ImagerTransformation.Create[0, 1, 0, -1, 0, raster.scanCount];
ENDCASE => ERROR Error; -- unsupported scanDirection
data.buffer ← NEW[TEXT[data.bytesPerLine]];
LoadBuffer[data, 0];
RETURN[data];
};
LoadBuffer:
PROC[data: Data, s:
NAT] ~ {
stream: STREAM ~ data.stream;
count: NAT ~ data.bytesPerLine;
index: INT ← data.rasterIndex+data.bytesPerLine*s;
IF data.linesPerBlock#0 THEN index ← index+data.bytesPadding*(s/data.linesPerBlock);
IO.SetIndex[stream, index];
IF IO.GetBlock[self: stream, block: data.buffer, count: count]=count THEN NULL
ELSE ERROR IO.EndOfStream[stream];
data.sBuffer ← s;
};
FromAIS:
PUBLIC
PROC[name:
ROPE]
RETURNS[PixelArray] ~ {
data: Data ~ Open[name];
RETURN[
NEW[PixelArrayRep ← [class: aisClass, data: data,
sSize: data.sSize, fSize: data.fSize, samplesPerPixel: data.samplesPerPixel, m: data.m]]];
};
FromAISSeparations:
PUBLIC
PROC[name0, name1, name2, name3:
ROPE ←
NIL]
RETURNS[PixelArray] ~ {
head, tail: LIST OF PixelArray ← NIL;
Append:
PROC[name:
ROPE] ~ {
pa: PixelArray ~ FromAIS[name];
list: LIST OF PixelArray ~ LIST[pa];
IF tail=NIL THEN head ← list ELSE tail.rest ← list;
tail ← list;
};
IF name0#NIL THEN Append[name0];
IF name1#NIL THEN Append[name1];
IF name2#NIL THEN Append[name2];
IF name3#NIL THEN Append[name3];
RETURN[ImagerPixelArray.JoinPixelArrays[head]];
};
aisClass: ImagerPixelArray.Class ~
NEW[ImagerPixelArray.ClassRep ← [
type: $AIS,
MaxSampleValue: AISMaxSampleValue,
GetSample: AISGetSample,
GetRow: AISGetRow
]];
AISMaxSampleValue:
PROC[pa: PixelArray, i:
NAT]
RETURNS[Val] ~ {
data: Data ~ NARROW[pa.data];
max: ARRAY [0..3] OF Val ~ [1B, 3B, 17B, 377B];
IF i IN[0..data.samplesPerPixel) THEN RETURN[max[data.lgBitsPerSample]]
ELSE ERROR RuntimeError.BoundsFault;
};
AISGetSample:
PROC[pa: PixelArray, s, f, i:
NAT ← 0]
RETURNS[Val] ~ {
data: Data ~ NARROW[pa.data];
IF s
IN[0..pa.sSize)
AND f
IN[0..pa.fSize)
AND i
IN[0..pa.samplesPerPixel)
THEN {
k: NAT ~ pa.samplesPerPixel*f+i;
IF data.sBuffer#s THEN LoadBuffer[data, s];
SELECT data.lgBitsPerSample
FROM
3 => RETURN[ORD[data.buffer[k]]];
ENDCASE => ERROR;
}
ELSE ERROR RuntimeError.BoundsFault;
};
AISGetRow:
PROC[pa: PixelArray, row: Row, s, f, i:
NAT ← 0] ~ {
data: Data ~ NARROW[pa.data];
IF s
IN[0..pa.sSize)
AND f
IN[0..pa.fSize)
AND i
IN[0..pa.samplesPerPixel)
THEN {
IF pa.samplesPerPixel=1
AND data.lgBitsPerSample=3
THEN {
count: NAT ~ MIN[row.size, pa.fSize-f];
stream: STREAM ~ data.stream;
index: INT ← data.rasterIndex+data.bytesPerLine*s;
IF data.linesPerBlock#0 THEN index ← index+data.bytesPadding*(s/data.linesPerBlock);
IO.SetIndex[stream, index];
TRUSTED {
base: LONG POINTER ~ LOOPHOLE[row, LONG POINTER]+SIZE[RowRep[0]];
IF IO.UnsafeGetBlock[self: stream, block: [base: base, count: count]]=count THEN NULL
ELSE ERROR IO.EndOfStream[stream];
};
}
ELSE ERROR;
}
ELSE ERROR RuntimeError.BoundsFault;
};
END.