ImagerPixelArrayAISImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, August 19, 1985 4:10:39 pm PDT
Doug Wyatt, March 7, 1986 4:21:51 pm PST
DIRECTORY
AISFileFormat USING [AttributeHeader, byteSizeAttributeHeader, byteSizeCommentPart, byteSizePartHeader, byteSizePhotometryPart, byteSizePlacementPart, byteSizeRasterPart, byteSizeUCACoding, bytesPerAISWord, CommentPart, nil, PartHeader, passwordValue, PhotometryPart, PlacementPart, RasterPart, UCACoding, wordsPerAISPage],
Basics USING [BITSHIFT, bitsPerByte, LongMult],
FS USING [StreamOpen],
ImagerPixelArray USING [Error, Join3, PixelArray],
ImagerPixelArrayAISPrivate USING [Data, DataRep],
ImagerPixelArrayPrivate USING [GetSamplesProc, MaxSampleValueProc, New, NewClass, PixelArrayClass],
ImagerSample USING [GetSamples, NewSampleMap, SampleMap, Vec],
ImagerTransformation USING [ScanMode, SFToXY, Transformation],
IO USING [EndOfStream, GetIndex, SetIndex, STREAM, UnsafeGetBlock],
Rope USING [FromProc, ROPE];
ImagerPixelArrayAISImpl: CEDAR MONITOR LOCKS data USING data: Data
IMPORTS Basics, FS, ImagerPixelArray, ImagerPixelArrayPrivate, ImagerSample, ImagerTransformation, IO, Rope
EXPORTS ImagerPixelArray
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
Data: TYPE ~ ImagerPixelArrayAISPrivate.Data;
DataRep: TYPE ~ ImagerPixelArrayAISPrivate.DataRep;
defaultLinesPerBlock: NAT ← 16;
ProduceError: PROC [explanation: ROPE] ~ {
ERROR ImagerPixelArray.Error[[code: $invalidAISFile, explanation: explanation]];
};
Assert: PROC [assertion: BOOL] ~ {
IF NOT assertion THEN ProduceError["AIS file structure is inconsistent."];
};
BytesForAISWords: PROC [aisWords: CARDINAL] RETURNS [INT] ~ INLINE {
RETURN[Basics.LongMult[AISFileFormat.bytesPerAISWord, aisWords]];
};
ReadBytes: UNSAFE PROC [stream: STREAM, base: LONG POINTER, bytes: INT] ~ UNCHECKED {
actualBytes: INT ~ IO.UnsafeGetBlock[stream, [base: LOOPHOLE[base], count: bytes]];
IF actualBytes#bytes THEN ERROR IO.EndOfStream[stream]; -- file too short
};
FromAIS: PUBLIC PROC [name: ROPE] RETURNS [PixelArray] ~ { OPEN AISFileFormat;
stream: STREAM ~ FS.StreamOpen[name];
data: Data ~ NEW[DataRep ← [stream: stream]];
header: AttributeHeader;
m: ImagerTransformation.Transformation ← NIL; -- from [s, f] to [x, y]
TRUSTED { ReadBytes[stream, @header, byteSizeAttributeHeader] };
IF header.password#passwordValue THEN ProduceError["AIS password value is wrong."];
Assert[header.length>0 AND (header.length MOD wordsPerAISPage)=0];
data.rasterSectionIndex ← BytesForAISWords[header.length];
FOR firstPart: BOOLTRUE, FALSE DO
partHeader: PartHeader;
startIndex: INT ~ IO.GetIndex[stream];
stopIndex: INT;
TRUSTED { ReadBytes[stream, @partHeader, byteSizePartHeader] };
stopIndex ← startIndex+BytesForAISWords[partHeader.length];
Assert[stopIndex<=data.rasterSectionIndex];
SELECT partHeader.type FROM
nil => { Assert[partHeader.length=0]; EXIT }; -- should be a zero word
raster => {
raster: RasterPart;
Assert[firstPart]; -- raster part must be first
TRUSTED { ReadBytes[stream, @raster, byteSizeRasterPart] };
Assert[IO.GetIndex[stream]<=stopIndex];
Assert[raster.scanCount>0 AND raster.scanLength>0 AND raster.samplesPerPixel>0];
data.raster ← NEW[RasterPart ← raster];
SELECT raster.codingType FROM
uca => {
uca: UCACoding;
byteSizeCoding: INT ~ stopIndex-IO.GetIndex[stream];
Assert[byteSizeCoding<=byteSizeUCACoding];
TRUSTED { ReadBytes[stream, @uca, byteSizeCoding] };
IF uca.bitsPerSample=0 THEN uca.bitsPerSample ← 1; -- kludge
Assert[INT[uca.bitsPerSample]*INT[raster.samplesPerPixel]*INT[raster.scanLength]
<=INT[Basics.bitsPerByte]*BytesForAISWords[uca.wordsPerScanLine]];
IF byteSizeCoding<byteSizeUCACoding THEN {
Assert[uca.scanLinesPerBlock=nil];
uca.paddingPerBlock ← nil;
};
data.uca ← NEW[UCACoding ← uca];
};
ENDCASE => ProduceError["Unknown AIS coding type."];
};
placement => {
placement: PlacementPart;
TRUSTED { ReadBytes[stream, @placement, byteSizePlacementPart] };
Assert[IO.GetIndex[stream]=stopIndex];
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];
Assert[data.placement=NIL];
data.placement ← NEW[PlacementPart ← placement];
};
photometry => {
photometry: PhotometryPart;
TRUSTED { ReadBytes[stream, @photometry, byteSizePhotometryPart] };
Assert[IO.GetIndex[stream]<=stopIndex];
IO.SetIndex[stream, stopIndex]; -- Ignore histogram, if any
Assert[data.photometry=NIL];
data.photometry ← NEW[PhotometryPart ← photometry];
};
comment => {
comment: CommentPart;
byteSizeComment: INT ~ stopIndex-IO.GetIndex[stream];
length: NAT ← 0; -- number of characters in the string
rope: ROPENIL;
Assert[byteSizeComment IN[1..byteSizeCommentPart]];
TRUSTED { ReadBytes[stream, @comment, byteSizeComment] };
length ← ORD[comment[0]];
Assert[byteSizeComment>=length+1];
{ -- 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 => IO.SetIndex[stream, stopIndex]; -- ignore unknown part type
IF IO.GetIndex[stream]=stopIndex THEN NULL
ELSE ERROR ImagerPixelArray.Error[[code: $bug, explanation: "Bug in FromAIS."]];
ENDLOOP;
IF data.raster#NIL AND data.uca#NIL THEN {
samplesPerPixel: NAT ~ data.raster.samplesPerPixel;
sSize: CARDINAL ~ data.raster.scanCount;
fSize: CARDINAL ~ data.raster.scanLength;
linesPerBlock: CARDINAL ← data.uca.scanLinesPerBlock;
bytesPadding: INT ← 0;
scanMode: ImagerTransformation.ScanMode;
data.bytesPerLine ← BytesForAISWords[data.uca.wordsPerScanLine];
IF linesPerBlock=nil THEN linesPerBlock ← MIN[defaultLinesPerBlock, sSize]
ELSE bytesPadding ← BytesForAISWords[data.uca.paddingPerBlock];
data.bytesPerBlock ← data.bytesPerLine*linesPerBlock+bytesPadding;
data.buffer ← ImagerSample.NewSampleMap[
size: [s: linesPerBlock, f: Basics.LongMult[samplesPerPixel, fSize]],
bitsPerSample: data.uca.bitsPerSample,
bitsPerLine: Basics.bitsPerByte*data.bytesPerLine
];
SELECT data.raster.scanDirection FROM
0, 8 => scanMode ← [slow: right, fast: up];
3 => scanMode ← [slow: down, fast: right];
ENDCASE => ERROR ImagerPixelArray.Error[[$unimplementedAISFile,
"AIS file has unimplemented scanDirection"]];
RETURN[ImagerPixelArrayPrivate.New[class: classAIS, data: data, immutable: TRUE,
samplesPerPixel: samplesPerPixel, sSize: sSize, fSize: fSize,
m: ImagerTransformation.SFToXY[scanMode: scanMode, sSize: sSize, fSize: fSize]
]];
}
ELSE { ProduceError["AIS file has no raster part."]; RETURN[NIL] };
};
classAIS: ImagerPixelArrayPrivate.PixelArrayClass ~ ImagerPixelArrayPrivate.NewClass[
type: $AIS,
MaxSampleValue: MaxSampleValueAIS,
GetSamples: GetSamplesAIS
];
MaxSampleValueAIS: ImagerPixelArrayPrivate.MaxSampleValueProc ~ {
-- PROC [self: PixelArray, i: NAT] RETURNS [Sample] --
data: Data ~ NARROW[self.data];
RETURN[Basics.BITSHIFT[1, data.uca.bitsPerSample]-1];
};
GetSamplesAIS: ImagerPixelArrayPrivate.GetSamplesProc ~ {
-- PROC [self: PixelArray, i: NAT, s, f: INT, samples: SampleBuffer, start: NAT, count: NAT] --
data: Data ~ NARROW[self.data];
entryGetSamplesAIS: ENTRY PROC [data: Data] ~ {
ENABLE UNWIND => NULL;
s0: CARDINAL ~ s; f0: CARDINAL ~ f;
samplesPerPixel: CARDINAL ~ data.raster.samplesPerPixel;
buffer: ImagerSample.SampleMap ~ data.buffer;
IF s0 NOT IN[data.smin..data.smax) THEN {
linesPerBlock: NAT ~ buffer.size.s;
block: NAT ~ s0/linesPerBlock; -- block number containing s0
byteIndex: INT ~ data.rasterSectionIndex+block*data.bytesPerBlock;
byteCount: INT ← 0;
data.smin ← block*linesPerBlock; -- first scan line in block
data.smax ← MIN[data.smin+linesPerBlock, data.raster.scanCount];
byteCount ← (data.smax-data.smin)*data.bytesPerLine;
IO.SetIndex[self: data.stream, index: byteIndex];
TRUSTED { ReadBytes[stream: data.stream, base: buffer.base.word, bytes: byteCount] };
};
ImagerSample.GetSamples[self: buffer,
min: [s: s0-data.smin, f: f0*samplesPerPixel+i], delta: [s: 0, f: samplesPerPixel],
samples: samples, start: start, count: count
];
};
entryGetSamplesAIS[data];
};
Join3AIS: PUBLIC PROC [name1, name2, name3: ROPE] RETURNS [PixelArray] ~ {
RETURN[ImagerPixelArray.Join3[FromAIS[name1], FromAIS[name2], FromAIS[name3]]];
};
END.