DitherAISImple.mesa
Mik Lamming September 29, 1982 2:44 pm
DIRECTORY
AIS    USING [CloseFile, CloseWindow, CreateFile, DeleteFile, FRef, GetWindowParams, OpenFile, OpenWindow, Raster, RasterPart, ReadRaster, ReadSample, WRef, WriteSample],
ConvertUnsafe USING [AppendRope],
DitherAIS,
Rope    USING [ROPE];
DitherAISImpl: PROGRAM
IMPORTS AIS, ConvertUnsafe
EXPORTS DitherAIS = BEGIN
OPEN DitherAIS;
Squash: PUBLIC PROCEDURE [redFn, greenFn, blueFn, rgbFn:Rope.ROPE, map:UserMapProc] = {
Transforms the three AIS files red,green and blue into the single AIS file: rgb using the user supplied map procedure and applying Floyd-Steinberg error distribution.
redString: LONG STRING ← [30];
greenString: LONG STRING ← [30];
blueString: LONG STRING ← [30];
rgbString: LONG STRING ← [30];
redFD, greenFD, blueFD, rgbFD:AIS.FRef;
temp, this, next:PixelBuf; -- 'this' holds current line's data, 'next - the one below
r:AIS.RasterPart;
redW, blueW, greenW, rgbW:AIS.WRef;
scan, top, bottom, left, right:CARDINAL;
Open the input files and get hold of the raster info. for use in generating the rgb ouput file
ConvertUnsafe.AppendRope[redString, redFn];
ConvertUnsafe.AppendRope[greenString, greenFn];
ConvertUnsafe.AppendRope[blueString, blueFn];
redFD ← AIS.OpenFile[redString];
redW ← AIS.OpenWindow[redFD];
greenFD ← AIS.OpenFile[greenString];
greenW ← AIS.OpenWindow[greenFD];
blueFD ← AIS.OpenFile[blueString];
blueW ← AIS.OpenWindow[blueFD];
Create the output file
AIS.ReadRaster[redFD, @r];
ConvertUnsafe.AppendRope[rgbString, rgbFn];
[] ← AIS.DeleteFile[rgbString];
rgbFD ← AIS.CreateFile[name:rgbString, raster:@r, overwrite:TRUE];
rgbW ← AIS.OpenWindow[rgbFD];
Create two working buffers for scan lines
[top, bottom, left, right] ← AIS.GetWindowParams[redW];
this ← NEW[PixelBufRec[right+2]]; -- make it a bit bigger to catch error propogation
next ← NEW[PixelBufRec[right+2]];
initialise the 'next' buffer with the first scan line
ReadRGB[redW, greenW, blueW, left, right, 1, next];
FOR scan IN [top..bottom] DO
advance the scan line buffers to the next line
temp ← this; this ← next; next ← temp;
IF scan < bottom THEN
ReadRGB[redW, greenW, blueW, left, right, scan+1, next];
SquashScan[this, next, left,right, map];
Writergb[rgbW, left, right, scan, this];
ENDLOOP;
AIS.CloseWindow[redW];
AIS.CloseWindow[blueW];
AIS.CloseWindow[greenW];
AIS.CloseWindow[rgbW];
AIS.CloseFile[redFD];
AIS.CloseFile[blueFD];
AIS.CloseFile[greenFD];
AIS.CloseFile[rgbFD];
};
Stretch: PUBLIC PROCEDURE [redFn, greenFn, blueFn, rgbFn:Rope.ROPE, unMap:UserUnmapProc] = {
Takes a dithered file rgbFn and cretates three separations using the unMap procedure
redString: LONG STRING ← [30];
greenString: LONG STRING ← [30];
blueString: LONG STRING ← [30];
rgbString: LONG STRING ← [30];
redFD, greenFD, blueFD, rgbFD:AIS.FRef;
raster:AIS.RasterPart;
redW, blueW, greenW, rgbW:AIS.WRef;
scan, pixel, top, bottom, left, right:CARDINAL;
r,g,b:CARDINAL;
Open the input file and get hold of the raster info. for use in generating the ouput files
ConvertUnsafe.AppendRope[redString, redFn];
ConvertUnsafe.AppendRope[greenString, greenFn];
ConvertUnsafe.AppendRope[blueString, blueFn];
ConvertUnsafe.AppendRope[rgbString, rgbFn];
rgbFD ← AIS.OpenFile[name:rgbString];
rgbW ← AIS.OpenWindow[rgbFD];
AIS.ReadRaster[rgbFD, @raster];
[] ← AIS.DeleteFile[redString];
redFD ← AIS.CreateFile[name:redString, raster:@raster, overwrite:TRUE];
redW ← AIS.OpenWindow[redFD];
[] ← AIS.DeleteFile[greenString];
greenFD ← AIS.CreateFile[name:greenString, raster:@raster, overwrite:TRUE];
greenW ← AIS.OpenWindow[greenFD];
[] ← AIS.DeleteFile[blueString];
blueFD ← AIS.CreateFile[name:blueString, raster:@raster, overwrite:TRUE];
blueW ← AIS.OpenWindow[blueFD];
[top, bottom, left, right] ← AIS.GetWindowParams[rgbW];
FOR scan IN [top..bottom] DO
FOR pixel IN [left..right] DO
[r,g,b] ← unMap[AIS.ReadSample[rgbW, scan, pixel]];
AIS.WriteSample[redW, r, scan, pixel];
AIS.WriteSample[greenW, g, scan, pixel];
AIS.WriteSample[blueW, b, scan, pixel];
ENDLOOP;
ENDLOOP;
AIS.CloseWindow[redW];
AIS.CloseWindow[blueW];
AIS.CloseWindow[greenW];
AIS.CloseWindow[rgbW];
AIS.CloseFile[redFD];
AIS.CloseFile[blueFD];
AIS.CloseFile[greenFD];
AIS.CloseFile[rgbFD];
};
SquashScan: PUBLIC PROCEDURE [this, next:PixelBuf, left,right:CARDINAL, map:UserMapProc] = {
Processes the pixel buffer 'this' using the user supplied map procedure 'map' to fill in the rgb field in each pixel of 'this' and modifies the contents of the buffer 'next' using Floyd-Steinberg error distribution
redError, greenError, blueError: INTEGER;
FOR pixel:CARDINAL IN [left..right] DO
[this.data[pixel].rgb, redError, greenError, blueError]
← map[this.data[pixel].red, this.data[pixel].green, this.data[pixel].blue];
use Floyd-Steinberg to distribute error to right and next line
this.data[pixel+1].red ← this.data[pixel+1].red + redError*3/8;
this.data[pixel+1].green ← this.data[pixel+1].green + greenError*3/8;
this.data[pixel+1].blue ← this.data[pixel+1].blue + blueError*3/8;
next.data[pixel].red ← next.data[pixel].red + redError*3/8;
next.data[pixel].green ← next.data[pixel].green + greenError*3/8;
next.data[pixel].blue ← next.data[pixel].blue + blueError*3/8;
next.data[pixel+1].red ← next.data[pixel+1].red + redError/4;
next.data[pixel+1].green ← next.data[pixel+1].green + greenError/4;
next.data[pixel+1].blue ← next.data[pixel+1].blue + blueError/4;
ENDLOOP
};

ReadRGB: PROCEDURE [r,g,b:AIS.WRef, left, right, scan:CARDINAL, buf:PixelBuf] = {
FOR i:CARDINAL IN [left..right] DO
buf.data[i].red ← AIS.ReadSample[r, scan, i];
buf.data[i].green ← AIS.ReadSample[g, scan, i];
buf.data[i].blue ← AIS.ReadSample[b, scan, i];
ENDLOOP;
};
Writergb: PROCEDURE [rgb:AIS.WRef, left, right, scan:CARDINAL, buf:PixelBuf] = {
FOR i:CARDINAL IN [left..right] DO
AIS.WriteSample[rgb, buf.data[i].rgb, scan, i];
ENDLOOP;
};
END.