<> <> <> <> 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 <> clipper.fMin _ clipper.fMin / displayData.displayClass.viewUnitsPerPixel; clipper.sMin _ clipper.sMin / displayData.displayClass.viewUnitsPerPixel; clipper.fSize _ clipper.fSize / displayData.displayClass.viewUnitsPerPixel; clipper.sSize _ clipper.sSize / displayData.displayClass.viewUnitsPerPixel; <> 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[]; }; }; <> 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; <> 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; <> 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 <> clipper.fMin _ clipper.fMin / displayData.displayClass.viewUnitsPerPixel; clipper.sMin _ clipper.sMin / displayData.displayClass.viewUnitsPerPixel; clipper.fSize _ clipper.fSize / displayData.displayClass.viewUnitsPerPixel; clipper.sSize _ clipper.sSize / displayData.displayClass.viewUnitsPerPixel; 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; }; <> 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; <> 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; <> 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; } <> 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.