-- AISIOImpl.mesa
Adapted from ImagerPixelArrayAISImpl
Copyright Ó 1985, 1990, 1992 by Xerox Corporation. All rights reserved.
Jules Bloomenthal May 22, 1991 5:37 pm PDT
Maureen Stone, May 21, 1991 12:33 pm PDT
DIRECTORY AISFileFormat, AISIO, Basics, ImagerSample, IO, PFS, Rope;
AISIOImpl: CEDAR PROGRAM
IMPORTS Basics, ImagerSample, IO, PFS, Rope
EXPORTS AISIO
~ BEGIN
Types and Constants
AttributeHeader:   TYPE ~ AISFileFormat.AttributeHeader;
PartHeader:    TYPE ~ AISFileFormat.PartHeader;
RasterPart:    TYPE ~ AISFileFormat.RasterPart;
UCACoding:    TYPE ~ AISFileFormat.UCACoding;
PlacementPart:   TYPE ~ AISFileFormat.PlacementPart;
PhotometryPart:   TYPE ~ AISFileFormat.PhotometryPart;
CommentPart:   TYPE ~ AISFileFormat.CommentPart;
Info:      TYPE ~ AISIO.Info;
Int16:       TYPE ~ Basics.HWORD;
Card16:      TYPE ~ Basics.HWORD;
RasterSampleMap:  TYPE ~ ImagerSample.RasterSampleMap;
SampleMap:    TYPE ~ ImagerSample.SampleMap;
SampleBuffer:   TYPE ~ ImagerSample.SampleBuffer;
STREAM:      TYPE ~ IO.STREAM;
ROPE:      TYPE ~ Rope.ROPE;
defaultLinesPerBlock: NAT ¬ 16;
Errors
Error: PUBLIC ERROR [reason: ROPE] = CODE;
Support
IOrd: PROC [h: Int16]  RETURNS [INT16] ~ --INLINE-- {RETURN[Basics.Int16FromH[h]]};
COrd: PROC [h: Card16] RETURNS [CARD16] ~ --INLINE-- {RETURN[Basics.Card16FromH[h]]};
Card: PROC [h: CARD16] RETURNS [Card16] ~ --INLINE-- {RETURN[Basics.HFromCard16[h]]};
Read Operations
Assert: PROC [title: ROPE, assertion: BOOL] ~ {
IF NOT assertion THEN Error[Rope.Cat["AIS file structure is inconsistent (", title, ")"]];
};
BytesForAISWords: PROC [aisWords: INT] RETURNS [INT] ~ INLINE {
RETURN[LONG[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
};
ReadInfo: PUBLIC PROC [name: ROPE] RETURNS [info: Info] ~ {
in: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[name], read];
header: MACHINE DEPENDENT RECORD [
c: AttributeHeader, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[AttributeHeader] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
partHeader: MACHINE DEPENDENT RECORD [
c: PartHeader, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[PartHeader] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
raster: MACHINE DEPENDENT RECORD [
c: RasterPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[RasterPart] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
uca: MACHINE DEPENDENT RECORD [
c: UCACoding, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[UCACoding] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
placement: MACHINE DEPENDENT RECORD [
c: PlacementPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[PlacementPart] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
photometry: MACHINE DEPENDENT RECORD [
c: PhotometryPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[PhotometryPart] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
comment: MACHINE DEPENDENT RECORD [
c: CommentPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[CommentPart] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
info ¬ NEW[AISIO.InfoRep];
TRUSTED {ReadBytes[in, @header, AISFileFormat.byteSizeAttributeHeader]};
IF header.c.password # AISFileFormat.passwordValue THEN Error["bad AIS password"];
Assert["header", COrd[header.c.length]>0 AND (COrd[header.c.length] MOD AISFileFormat.wordsPerAISPage)=0];
info.rasterOffset ¬ BytesForAISWords[COrd[header.c.length]];
FOR firstPart: BOOL ¬ TRUE, FALSE DO
startIndex, stopIndex: INT ¬ IO.GetIndex[in];
TRUSTED {ReadBytes[in, @partHeader, AISFileFormat.byteSizePartHeader]};
stopIndex ¬startIndex+BytesForAISWords[256*partHeader.c.lengthHi+partHeader.c.lengthLo];
Assert["rasterSection", stopIndex <= info.rasterOffset];
SELECT partHeader.c.type FROM
nil => {
Assert["part length", partHeader.c.lengthHi = 0 AND partHeader.c.lengthLo = 0];
EXIT; -- only correct way out
}; -- should be a zero word
raster => {
Assert["raster first", firstPart]; -- raster part must be first
TRUSTED {ReadBytes[in, @raster, AISFileFormat.byteSizeRasterPart]};
Assert["stop", IO.GetIndex[in] <= stopIndex];
Assert["raster", COrd[raster.c.scanCount]>0 AND COrd[raster.c.scanLength]>0 AND COrd[raster.c.samplesPerPixel]>0];
SELECT raster.c.codingType FROM
uca => {
byteSizeCoding: INT ~ stopIndex-IO.GetIndex[in];
Assert["byteSizeCoding", byteSizeCoding <= AISFileFormat.byteSizeUCACoding];
TRUSTED {ReadBytes[in, @uca, byteSizeCoding]};
IF COrd[uca.c.bitsPerSample] = 0 THEN uca.c.bitsPerSample.lo ¬ 1; -- kludge
MessageWindow.Append[IO.PutFR["bitspersample: %g", IO.int[INT[COrd[uca.c.bitsPerSample]]]], TRUE];
MessageWindow.Append[IO.PutFR["samplesPerPixel: %g", IO.int[INT[COrd[raster.c.samplesPerPixel]]]], TRUE];
MessageWindow.Append[IO.PutFR["scanLength: %g", IO.int[INT[COrd[raster.c.scanLength]]]], TRUE];
MessageWindow.Append[IO.PutFR["bitsPerByte: %g", IO.int[INT[Basics.bitsPerByte]]], TRUE];
MessageWindow.Append[IO.PutFR["wordsPerScanLine: %g", IO.int[INT[COrd[uca.c.wordsPerScanLine]]]], TRUE];
Assert["scans",INT[COrd[uca.c.bitsPerSample]]*INT[COrd[raster.c.samplesPerPixel]]*INT[COrd[raster.c.scanLength]]<=INT[Basics.bitsPerByte]*BytesForAISWords[COrd[uca.c.wordsPerScanLine]]];
IF byteSizeCoding < AISFileFormat.byteSizeUCACoding THEN {
Assert["scanLinesPerBlock", uca.c.scanLinesPerBlock = AISFileFormat.nil];
uca.c.paddingPerBlock ¬ AISFileFormat.nil;
};
info.uca ¬ NEW[UCACoding ¬ uca.c];
};
ENDCASE => Error["Unknown AIS coding type"];
info.raster ¬ NEW[RasterPart ¬ raster.c];
};
placement => {
TRUSTED {ReadBytes[in, @placement, AISFileFormat.byteSizePlacementPart]};
Assert["placement", IO.GetIndex[in] = stopIndex];
IF IOrd[placement.c.xLeft] = -1 AND IOrd[placement.c.yBottom] = -1 AND IOrd[placement.c.xWidth] = -1 AND IOrd[placement.c.yHeight] = -1
THEN NULL
ELSE Assert["place", IOrd[placement.c.xWidth]>0 AND IOrd[placement.c.yHeight]>0];
info.placement ¬ NEW[PlacementPart ¬ placement.c];
};
photometry => {
TRUSTED {ReadBytes[in, @photometry, AISFileFormat.byteSizePhotometryPart]};
Assert["stop", IO.GetIndex[in] <= stopIndex];
IO.SetIndex[in, stopIndex]; -- Ignore histogram, if any
info.photometry ¬ NEW[PhotometryPart ¬ photometry.c];
};
comment => {
byteSizeComment: INT ~ stopIndex-IO.GetIndex[in];
length: NAT ¬ 0; -- number of characters in the string
Assert["comment", byteSizeComment IN [1..AISFileFormat.byteSizeCommentPart]];
TRUSTED {ReadBytes[in, @comment, byteSizeComment]};
length ¬ ORD[comment.c[0]];
Assert["comment", byteSizeComment >= INT[length+1]];
{ -- turn the comment into a ROPE
i: NAT ¬ 0;
p: PROC RETURNS [CHAR] ~ {RETURN[comment.c[i ¬ i+1]]};
rope: ROPE ¬ Rope.FromProc[len: length, p: p];
info.comments ¬ CONS[rope, info.comments];
};
};
ENDCASE => IO.SetIndex[in, stopIndex]; -- ignore unknown part type
IF IO.GetIndex[in] # stopIndex THEN Error["Bug in FromAIS"];
ENDLOOP;
IO.Close[in];
};
Read: PUBLIC PROC [name: ROPE, info: Info ¬ NIL] RETURNS [map: SampleMap ¬ NIL] ~ {
i: Info ¬ ReadInfo[name];
in: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[name], read];
IF info # NIL THEN info­ ¬ i­;
IF i.raster # NIL AND i.uca # NIL
THEN {
samplesPerPixel: NAT ~ COrd[i.raster.samplesPerPixel];
size: ImagerSample.Vec ¬ [COrd[i.raster.scanCount], COrd[i.raster.scanLength]];
bytesPerLine: INT ¬ BytesForAISWords[COrd[i.uca.wordsPerScanLine]];
bytesPadding: INT ¬ IF i.uca.scanLinesPerBlock = AISFileFormat.nil
THEN 0
ELSE BytesForAISWords[COrd[i.uca.paddingPerBlock]];
linesPerBlock: INT ¬ IF i.uca.scanLinesPerBlock = AISFileFormat.nil
THEN MIN[defaultLinesPerBlock, size.s]
ELSE COrd[i.uca.scanLinesPerBlock];
bytesPerBlock: INT ¬ bytesPerLine*linesPerBlock+bytesPadding;
buf: RasterSampleMap ¬ ImagerSample.NewSampleMap[
box: [min: [0, 0], max: [s: linesPerBlock, f: samplesPerPixel*size.f]],
bitsPerSample: 8, -- COrd[i.uca.bitsPerSample],
bitsPerLine: Basics.bitsPerByte*bytesPerLine
];
base: LONG POINTER TO WORD ¬ ImagerSample.GetBase[buf].word;
map ¬ ImagerSample.NewSampleMap[
box: [min: [0, 0], max: [s: size.s, f: samplesPerPixel*size.f]],
bitsPerSample: COrd[i.uca.bitsPerSample],
bitsPerLine: Basics.bitsPerByte*bytesPerLine];
SELECT COrd[i.raster.scanDirection] FROM
0, 3, 8 => NULL;
ENDCASE => Error["AIS file has unimplemented scanDirection"];
IO.SetIndex[in, i.rasterOffset];
FOR s: NAT IN [0..size.s) DO
TRUSTED {ReadBytes[in, base, bytesPerLine]};
ImagerSample.BasicTransfer[map, buf, [s, 0], [0, 0], [1, size.f]];
ENDLOOP;
}
ELSE Error["AIS file has no raster part."];
IO.Close[in];
};
Write Operations
WriteBytes: UNSAFE PROC [out: IO.STREAM, base: LONG POINTER, bytes: INT] ~ UNCHECKED {
IO.UnsafePutBlock[out, [base: LOOPHOLE[base], count: bytes]];
};
Write: PUBLIC PROC [
name: ROPE,
map: SampleMap,
placement: AISIO.Placement ¬ NIL,
photometry: AISIO.Photometry ¬ NIL,
comments: LIST OF ROPE ¬ NIL]
~ {
WritePartHeader: PROC [type: AISFileFormat.PartType, nBytesInPart: INT] ~ {
nWordsTotal: INT ¬ (nBytesInPart+AISFileFormat.byteSizePartHeader)/2;
part: MACHINE DEPENDENT RECORD [
c: PartHeader, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[PartHeader] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
part.c ¬ [type, nWordsTotal/256, nWordsTotal MOD 256];
IF type = nil THEN part.c.lengthHi ¬ part.c.lengthLo ¬ 0;
TRUSTED {WriteBytes[out, @part, AISFileFormat.byteSizePartHeader]};
};
wordsHeader: INT ¬ 2048;
out: IO.STREAM ~ PFS.StreamOpen[PFS.PathFromRope[name], write];
header: MACHINE DEPENDENT RECORD [
c: AttributeHeader, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[AttributeHeader] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
raster: MACHINE DEPENDENT RECORD [
c: RasterPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[RasterPart] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
uca: MACHINE DEPENDENT RECORD [
c: UCACoding, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[UCACoding] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
box: ImagerSample.Box ¬ ImagerSample.GetBox[map];
size: ImagerSample.Vec ¬ ImagerSample.GetSize[map];
bpp: ImagerSample.BitsPerSample ¬ MAX[1, ImagerSample.GetBitsPerSample[map]];
aisWordsPerLine: INT ¬ (size.f*bpp+15)/16;
buf: RasterSampleMap ¬ ImagerSample.NewSampleMap[[max: [1, size.f]], bpp];
base: LONG POINTER TO WORD ¬ ImagerSample.GetBase[buf].word;
IF MIN[size.f, size.s] = 0 THEN Error["no pixels in map"];
header.c ¬ [AISFileFormat.passwordValue, Card[wordsHeader]];
raster.c ¬ [Card[size.s], Card[size.f], Card[3], Card[1], 0, uca, AISFileFormat.nil];
uca.c ¬ [Card[bpp], Card[aisWordsPerLine], AISFileFormat.nil, AISFileFormat.nil];
TRUSTED {WriteBytes[out, @header, AISFileFormat.byteSizeAttributeHeader]};
WritePartHeader[raster, AISFileFormat.byteSizeRasterPart+AISFileFormat.byteSizeUCACoding];
TRUSTED {WriteBytes[out, @raster, AISFileFormat.byteSizeRasterPart]};
TRUSTED {WriteBytes[out, @uca, AISFileFormat.byteSizeUCACoding]};
IF placement # NIL THEN {
part: MACHINE DEPENDENT RECORD [
c: AISFileFormat.PlacementPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[UCACoding] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
part.c ¬ placement­;
WritePartHeader[placement, AISFileFormat.byteSizePlacementPart];
TRUSTED {WriteBytes[out, @part, AISFileFormat.byteSizePlacementPart]};
};
IF photometry # NIL THEN {
part: MACHINE DEPENDENT RECORD [
c: AISFileFormat.PhotometryPart, p: PACKED ARRAY [0..(BITS[WORD]-(BITS[UCACoding] MOD BITS[WORD])) MOD BITS[WORD]) OF [0..1]];
part.c ¬ photometry­;
WritePartHeader[photometry, AISFileFormat.byteSizePhotometryPart];
TRUSTED {WriteBytes[out, @part, AISFileFormat.byteSizePhotometryPart]};
};
IF comments # NIL THEN {--ugh--};
WritePartHeader[nil, 0]; -- end header
IO.SetIndex[out, 2*wordsHeader];
FOR s: NAT IN [box.min.s..box.max.s) DO
ImagerSample.BasicTransfer[buf, map, [0, 0], [s, box.min.f], [1, size.f]];
TRUSTED {WriteBytes[out, base, aisWordsPerLine*2]};
ENDLOOP;
IO.Close[out];
};
Convenience Procs for Converters
NewSampleMap: PUBLIC PROC [scanCount: CARDINAL, scanLength: CARDINAL, bitsPerPixel: [0..8] ¬ 8]
RETURNS [map: SampleMap]
~ {
map ¬ ImagerSample.NewSampleMap[box: [[s: 0, f: 0], [s: scanCount, f: scanLength]], bitsPerSample: bitsPerPixel, bitsPerLine: 0];
};
ReadSample: PUBLIC PROC [map: SampleMap, line, pixel: CARDINAL] RETURNS [value: CARDINAL] ~ {
value ¬ ImagerSample.Get[map: map, index: [s: line, f: pixel]];
};
WriteSample: PUBLIC PROC [map: SampleMap, value: CARDINAL, line, pixel: CARDINAL] ~ {
ImagerSample.Put[map: map, index: [s: line, f: pixel], value: value];
};
ReadLine: PUBLIC PROC [map: SampleMap, buffer: SampleBuffer, line: CARDINAL] ~ {
ImagerSample.GetSamples[map: map, buffer: buffer, initIndex: [s: line, f: 0]];
};
WriteLine: PUBLIC PROC [map: SampleMap, buffer: SampleBuffer, line: CARDINAL] ~ {
ImagerSample.PutSamples[map: map, buffer: buffer, initIndex: [s: line, f: 0]];
};
END.