ImagerColorAISImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, August 16, 1984 10:17:34 am PDT
Last Edited by: Stone, August 15, 1984 7:09:46 pm PDT
DIRECTORY
Real USING [RoundI, Float],
Rope USING [ROPE],
Basics USING [bitsPerWord, BITSHIFT],
FS USING [StreamOpen],
IO USING [STREAM, UnsafeBlock, UnsafeGetBlock, UnsafePutBlock, Close],
Imager USING [Context],
ImagerBasic USING [DeviceRectangle],
ImagerDisplay USING [DisplayData],
ImagerPixelMapsExtras USING [LoadScanSeg, StoreScanSeg, ByteSequence],
ImagerColorAIS;
ImagerColorAISImpl:
CEDAR
PROGRAM
IMPORTS FS, IO, Real, Basics, ImagerPixelMapsExtras
EXPORTS ImagerColorAIS
~ BEGIN
bytesPerWord: NAT ~ Basics.bitsPerWord / 8;
maxBytesPerBlock: NAT ~ 8192;
aisPassword: INTEGER ~ -31574;
typeDo: INTEGER ~ 17519; -- ASCII for "D0"?
PartHeader: TYPE ~ MACHINE DEPENDENT RECORD[ type: [0..64), length: [0..1024) ];
AISHeader: TYPE ~ RECORD[ passWord: INTEGER, length: NAT, partHeader: PartHeader];
AISRasterDef: TYPE ~ RECORD[ scanCount, scanLength, scanDir, samplesPerPixel, codingType,
bitsPerSample, wordsPerScanLine, scanLinesPerBlock,
paddingPerBlock: NAT ];
ByteSequence: TYPE ~ ImagerPixelMapsExtras.ByteSequence;
DisplayData: TYPE ~ ImagerDisplay.DisplayData;
ColorAISError: PUBLIC SIGNAL [reason: ATOM] ~ CODE;
Ceiling:
PROC[number:
REAL]
RETURNS[result:
INTEGER] ~ {
result ← Real.RoundI[number];
IF result < number THEN result ← result + 1;
};
GetAISFile: PUBLIC PROC[context: Imager.Context, fileName: Rope.ROPE,
xOffset, yOffset:
INTEGER ← 0] ~ {
GetFiles[context, fileName, NIL, NIL, xOffset, yOffset];
};
Get3AISFiles: PUBLIC PROC[context: Imager.Context,
redFile, greenFile, blueFile: Rope.ROPE,
xOffset, yOffset:
INTEGER ← 0] ~ {
GetFiles[context, redFile, greenFile, blueFile, xOffset, yOffset];
};
GetFiles: PROC[context: Imager.Context, aisFile, greenFile, blueFile: Rope.ROPE,
xOffset, yOffset:
INTEGER ← 0] ~
TRUSTED {
displayData: DisplayData ← NARROW[ context.data, DisplayData]; -- get context info
clipper: ImagerBasic.DeviceRectangle ← displayData.compositeClipper.first;
input: IO.STREAM ← FS.StreamOpen[aisFile];
inputRed, inputGrn, inputBlu: IO.STREAM;
block: REF ByteSequence ← NEW[ByteSequence[maxBytesPerBlock]];
block2: REF ByteSequence ← NEW[ByteSequence[maxBytesPerBlock]];
blockPtr: LONG POINTER;
unsafeBlock: IO.UnsafeBlock;
bytesRead: INT;
header: REF AISHeader;
raster: REF AISRasterDef;
scan: INTEGER;
index, scanCount, pixelsPerScanline, bitsPerSample, samplesPerPixel, samplesPerByte: NAT;
wordsPerScanLine, blockSize, paddingPerBlock, codingType: NAT;
blocked: BOOLEAN;
scanLinesPerBlock:
INTEGER;
-- -1 means no blocking
Read 1024 word file header and set up to read image
unsafeBlock.base ← LOOPHOLE[LOOPHOLE[block, LONG POINTER] + 1, LONG POINTER];
unsafeBlock.startIndex ← 0; unsafeBlock.count ← 2048; -- range of bytes to fill
bytesRead ← IO.UnsafeGetBlock[ input, unsafeBlock];
header ← LOOPHOLE[unsafeBlock.base, REF AISHeader]; -- look at block in AIS format
IF header.passWord # aisPassword -- check password
THEN { ColorAISError[$NotAnIASFile]; RETURN[]; };
IF header.length > 1024
THEN {
-- read rest of header if long
unsafeBlock.startIndex ← 2048;
unsafeBlock.count ← header.length * bytesPerWord - 2048;
IF unsafeBlock.count + unsafeBlock.startIndex > maxBytesPerBlock
THEN {
ColorAISError[$ExcessivelyLongAISHeader]; RETURN[]; };
bytesRead ← IO.UnsafeGetBlock[ input, unsafeBlock];
};
index ← 0;
WHILE header.partHeader.type # 1
DO
-- search for raster description
index ← index + header.partHeader.length*bytesPerWord;
IF index > (unsafeBlock.startIndex + unsafeBlock.count)/bytesPerWord
THEN {
ColorAISError[$NoRasterDescription]; RETURN[]; };
header ← LOOPHOLE[unsafeBlock.base + index, REF AISHeader];
ENDLOOP;
raster ← LOOPHOLE[unsafeBlock.base + index + 3, REF AISRasterDef];
scanCount ← raster.scanCount;
pixelsPerScanline ← raster.scanLength; -- Pixels per scanline
IF raster.scanDir # 3
THEN {
-- check for right and down scan direction
ColorAISError[$NotAVideoRaster]; RETURN[]; };
samplesPerPixel ← raster.samplesPerPixel;
codingType ← raster.codingType;
bitsPerSample ← Basics.BITSHIFT[1, displayData[0].refRep.lgBitsPerPixel];
IF bitsPerSample = 16 THEN bitsPerSample ← 8; -- 16 means interleaved RG on Dorado
IF bitsPerSample # raster.bitsPerSample
THEN {
ColorAISError[$BitsPerSampleMismatched]; RETURN[];
};
bitsPerSample ← raster.bitsPerSample;
samplesPerByte ← 8 / bitsPerSample;
wordsPerScanLine ← raster.wordsPerScanLine;
IF raster.scanLinesPerBlock < 32768 -- -1 means unblocked
THEN { scanLinesPerBlock ← raster.scanLinesPerBlock;
-- blocked
paddingPerBlock ← raster.paddingPerBlock * bytesPerWord;
blocked ← TRUE; }
ELSE { scanLinesPerBlock ← maxBytesPerBlock / (wordsPerScanLine * bytesPerWord);
paddingPerBlock ← 0; -- unblocked
blocked ← FALSE; };
xOffset ← xOffset + (clipper.fSize - LOOPHOLE[pixelsPerScanline, INTEGER])/2;
yOffset ← yOffset + (clipper.sSize - LOOPHOLE[scanCount, INTEGER])/2;
IF greenFile #
NIL
AND blueFile #
NIL
THEN {
-- get green and blue files, if there
header ← LOOPHOLE[unsafeBlock.base, REF AISHeader]; -- point header record at block start
inputRed ← input;
inputGrn ← FS.StreamOpen[greenFile];
bytesRead ← IO.UnsafeGetBlock[ inputGrn, unsafeBlock];
IF header.passWord # aisPassword -- check password
THEN { ColorAISError[$GreenFileNotAnIASFile]; RETURN[]; };
inputBlu ← FS.StreamOpen[blueFile];
bytesRead ← IO.UnsafeGetBlock[ inputBlu, unsafeBlock];
IF header.passWord # aisPassword -- check password
THEN { ColorAISError[$BlueFileNotAnIASFile]; RETURN[]; };
};
Now go get the image bits
scan ← yOffset;
scanCount ← MIN[scanCount + scan, clipper.sSize];
unsafeBlock.count ← blockSize ←
wordsPerScanLine * bytesPerWord * scanLinesPerBlock + paddingPerBlock;
IF greenFile #
NIL
AND blueFile #
NIL
AND blockSize*3 > maxBytesPerBlock
THEN {
block ← NEW[ByteSequence[blockSize*3]];
block2 ← NEW[ByteSequence[blockSize*3]];
unsafeBlock.base ← LOOPHOLE[block, LONG POINTER] + SIZE[ByteSequence[0]];
};
WHILE scan < scanCount
DO
-- read image bits
IF greenFile = NIL OR blueFile = NIL -- read block of scanlines
THEN bytesRead ← IO.UnsafeGetBlock[ input, unsafeBlock]
ELSE {
-- read from 3 files
bytesRead ← IO.UnsafeGetBlock[ inputRed, unsafeBlock];
unsafeBlock.count ← blockSize;
unsafeBlock.startIndex ← blockSize;
bytesRead ← IO.UnsafeGetBlock[ inputGrn, unsafeBlock];
unsafeBlock.count ← blockSize;
unsafeBlock.startIndex ← 2 * blockSize;
bytesRead ← IO.UnsafeGetBlock[ inputBlu, unsafeBlock];
unsafeBlock.count ← blockSize;
unsafeBlock.startIndex ← 0;
};
FOR i:
NAT
IN [0..scanLinesPerBlock)
DO
IF codingType = typeDo
THEN blockPtr ←
-- already in Dorado 24-bit format
unsafeBlock.base + i * wordsPerScanLine
ELSE IF greenFile # NIL AND blueFile # NIL
THEN {
-- scanline sequential, reformat for Dorado
rSrc, gSrc, bSrc, rDst, gDst, bDst: NAT;
rSrc ← i * wordsPerScanLine * bytesPerWord; rDst ← 0;
gSrc ← rSrc + blockSize; gDst ← rDst + 1;
bSrc ← rSrc + 2*blockSize; bDst ← rDst + 2*pixelsPerScanline;
FOR j:
NAT
IN [0..pixelsPerScanline)
DO
block2[rDst] ← block[rSrc]; rDst ← rDst + 2; rSrc ← rSrc + 1;
block2[gDst] ← block[gSrc]; gDst ← gDst + 2; gSrc ← gSrc + 1;
block2[bDst] ← block[bSrc]; bSrc ← bSrc + 1; bDst ← bDst + 1;
ENDLOOP;
set pointer to new bit location
blockPtr ← LOOPHOLE[block2, LONG POINTER] + SIZE[ByteSequence[0]];
}
ELSE
IF samplesPerPixel = 1
THEN blockPtr ←
-- straightforward case
unsafeBlock.base + i * wordsPerScanLine
ELSE
IF samplesPerPixel = 3
THEN {
-- interleaved RGB, 3 samples per pixel
rSrc, gSrc, bSrc, rDst, gDst, bDst: NAT;
rSrc ← i * wordsPerScanLine * bytesPerWord; rDst ← 0;
gSrc ← rSrc + 1; gDst ← rDst + 1;
bSrc ← rSrc + 2; bDst ← rDst + 2*pixelsPerScanline;
FOR j:
NAT
IN [0..pixelsPerScanline)
DO
block2[rDst] ← block[rSrc]; rDst ← rDst + 2; rSrc ← rSrc + 3;
block2[gDst] ← block[gSrc]; gDst ← gDst + 2; gSrc ← gSrc + 3;
block2[bDst] ← block[bSrc]; bDst ← bDst + 1; bSrc ← bSrc + 3;
ENDLOOP;
set pointer to new bit location
blockPtr ← LOOPHOLE[block2, LONG POINTER] + SIZE[ByteSequence[0]];
}
ELSE
IF samplesPerPixel = 4
THEN {
ColorAISError[$InterleavedRGBalphaUnimplemented];
RETURN[];
};
IF scan >= 0
AND scan < scanCount
THEN
FOR j:
NAT
IN [0..
MIN[2, displayData.numberOfSeparations])
DO
-- get all 24-bits
pxlIndex: CARDINAL ← MAX[0, -xOffset];
wndwIndex: CARDINAL ← MAX[0, xOffset];
ImagerPixelMapsExtras.LoadScanSeg[
displayData[j],
clipper.sMin + scan,
clipper.fMin + wndwIndex,
MIN[clipper.fSize - wndwIndex, pixelsPerScanline - pxlIndex],
blockPtr,
pxlIndex + (j * 2*pixelsPerScanline)
];
ENDLOOP;
scan ← scan + 1;
ENDLOOP;
ENDLOOP
};
PutFastAISFile:
PUBLIC
PROC[context: Imager.Context, fileName: Rope.
ROPE] ~ {
PutFile[context, fileName, NIL, NIL, TRUE];
};
PutAISFile:
PUBLIC
PROC[context: Imager.Context, fileName: Rope.
ROPE] ~ {
PutFile[context, fileName, NIL, NIL, FALSE];
};
Put3AISFiles:
PUBLIC
PROC[context: Imager.Context, redFile, greenFile, blueFile: Rope.
ROPE] ~ {
PutFile[context, redFile, greenFile, blueFile, FALSE];};
PutFile:
PROC[context: Imager.Context, fileName, greenFile, blueFile: Rope.
ROPE, fast:
BOOLEAN] ~
TRUSTED {
displayData: DisplayData ← NARROW[ context.data, DisplayData]; -- get context info
clipper: ImagerBasic.DeviceRectangle ← displayData.compositeClipper.first;
output: ARRAY[0..3) OF IO.STREAM;
numFiles: NAT ← 1;
block: REF ByteSequence ← NEW[ByteSequence[maxBytesPerBlock]];
block2: REF ByteSequence ← NEW[ByteSequence[maxBytesPerBlock]];
header: REF AISHeader;
raster: REF AISRasterDef;
unsafeBlock: IO.UnsafeBlock;
scan: INTEGER;
scanCount, samplesPerByte, samplesPerPixel, wordsPerScanLine, pixelsPerScanline: NAT;
scanLinesPerBlock: INTEGER; -- -1 means no blocking
output[0] ← FS.StreamOpen[fileName, create];
IF (greenFile #
NIL)
AND (blueFile #
NIL)
THEN {
output[1] ← FS.StreamOpen[greenFile, create];
output[2] ← FS.StreamOpen[blueFile, create];
numFiles ← 3;
};
Write 1024 word file header
unsafeBlock.base ← LOOPHOLE[block, LONG POINTER] + SIZE[ByteSequence[0]];
unsafeBlock.startIndex ← 0;
unsafeBlock.count ← 2048;
FOR i: NAT IN [0..1024) DO block[i] ← 0; ENDLOOP; -- zero block for safety
header ← LOOPHOLE[unsafeBlock.base, REF AISHeader]; -- look at block in AIS format
raster ← LOOPHOLE[unsafeBlock.base + 3, REF AISRasterDef];
header.passWord ← aisPassword;
header.length ← 1024;
header.partHeader.type ← 1;
header.partHeader.length ← 10;
raster.scanCount ← displayData[0].sSize; -- scanCount
raster.scanLength ← pixelsPerScanline ← displayData[0].fSize; -- scanLength
raster.scanDir ← 3; -- scanDir (right-down)
raster.samplesPerPixel ← samplesPerPixel
←
-- samples/pixel
IF (displayData.displayClass.displayType = $Std24bpp) AND (numFiles = 1) THEN 3 ELSE 1;
raster.codingType ← IF fast AND (raster.samplesPerPixel = 3)
THEN typeDo ELSE 1; -- coding type (UnCompressedArray)
raster.bitsPerSample ← Basics.BITSHIFT[1, displayData[0].refRep.lgBitsPerPixel]; -- bits/sample
raster.bitsPerSample ← MIN[8, raster.bitsPerSample];
samplesPerByte ← 8 / raster.bitsPerSample;
raster.wordsPerScanLine ← wordsPerScanLine ← Ceiling[ --
words/ScanLine
Real.Float[displayData[0].fSize * samplesPerPixel] / (bytesPerWord * samplesPerByte)];
LOOPHOLE[raster.scanLinesPerBlock, INTEGER] ← -1; -- ScanLines/block (unblocked)
raster.paddingPerBlock ← 0; -- padding/block
FOR i:
NAT
IN [0..numFiles)
DO
IO.UnsafePutBlock[ output[i], unsafeBlock]; -- write out block
ENDLOOP;
Now output the image itself
scan ← displayData[0].sMin;
scanLinesPerBlock ← (maxBytesPerBlock * samplesPerByte / samplesPerPixel)
/ displayData[0].fSize;
scanLinesPerBlock ← scanLinesPerBlock / numFiles;
scanCount ← displayData[0].sSize + scan;
unsafeBlock.startIndex ← 0;
unsafeBlock.count ← scanLinesPerBlock * wordsPerScanLine * bytesPerWord;
WHILE scan < scanCount
DO
-- write image bits
FOR i:
NAT
IN [0..scanLinesPerBlock)
DO
IF scan < scanCount
THEN {
blockPtr:
LONG
POINTER ←
IF displayData.displayClass.displayType = $Std24bpp AND NOT fast
THEN
LOOPHOLE[block2, LONG POINTER] + SIZE[ByteSequence[0]]
ELSE
LOOPHOLE[block, LONG POINTER] + i * wordsPerScanLine + SIZE[ByteSequence[0]];
FOR j:
NAT
IN [0..
MIN[2, displayData.numberOfSeparations])
DO
-- get all 24 bits
ImagerPixelMapsExtras.StoreScanSeg[
-- store scanline in "
blockPtr
"
displayData[j],
scan,
clipper.fMin,
clipper.fSize,
blockPtr,
j * 2*pixelsPerScanline
];
ENDLOOP;
Interleave RGB in one long file (untested)
IF (displayData.displayClass.displayType = $Std24bpp
AND
NOT fast )
AND (numFiles = 1)
THEN {
rSrc, gSrc, bSrc, rDst, gDst, bDst: NAT;
rDst ← i * wordsPerScanLine * bytesPerWord; rSrc ← 0;
gDst ← rDst + 1; gSrc ← rSrc + 1;
bDst ← rDst + 2; bSrc ← rSrc + 2*pixelsPerScanline;
FOR j:
NAT
IN [0..pixelsPerScanline)
DO
block[rDst] ← block2[rSrc]; rDst ← rDst + 3; rSrc ← rSrc + 2;
block[gDst] ← block2[gSrc]; gDst ← gDst + 3; gSrc ← gSrc + 2;
block[bDst] ← block2[bSrc]; bDst ← bDst + 3; bSrc ← bSrc + 1;
ENDLOOP;
}
Output three files for color (compatible with longstanding default)
ELSE
IF displayData.displayClass.displayType = $Std24bpp
AND
NOT fast
THEN {
rSrc, gSrc, bSrc, rDst, gDst, bDst: NAT;
rDst ← i * wordsPerScanLine * bytesPerWord; rSrc ← 0;
gDst ← rDst + unsafeBlock.count; gSrc ← rSrc + 1;
bDst ← rDst + 2 * unsafeBlock.count; bSrc ← rSrc + 2*pixelsPerScanline;
FOR j:
NAT
IN [0..pixelsPerScanline)
DO
block[rDst] ← block2[rSrc]; rSrc ← rSrc + 2; rDst ← rDst + 1;
block[gDst] ← block2[gSrc]; gSrc ← gSrc + 2; gDst ← gDst + 1;
block[bDst] ← block2[bSrc]; bDst ← bDst + 1; bSrc ← bSrc + 1;
ENDLOOP;
};
scan ← scan + 1;
};
ENDLOOP;
FOR j:
NAT
IN [0..numFiles)
DO
IO.UnsafePutBlock[ output[j], unsafeBlock]; -- write scanlines, unsafeBlock is really block
unsafeBlock.startIndex ← unsafeBlock.startIndex + unsafeBlock.count;
ENDLOOP;
unsafeBlock.startIndex ← 0;
ENDLOOP;
FOR i:
NAT
IN [0..numFiles)
DO
IO.Close[output[i]]; -- flush all output and close file
ENDLOOP;
};
END.