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.