AISCopyImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Mik Lamming - January 30, 1984 11: 58: 25 am PST
Last Edited by: Spreitzer, July 21, 1984 5: 28: 59 pm PDT
Tim Diebert: October 25, 1985 10:58:30 am PDT
Rick Beach, May 29, 1985 6:25:07 pm PDT (CedarChest6.0)
DIRECTORY
AIS,
AISCopy,
Buttons,
Commander,
Containers,
ExtraAIS,
Icons,
IO,
FS,
Imager,
ImagerBackdoor USING [GetBounds, invert],
ImagerColorOperator USING [GrayLinearColorModel],
ImagerPixelArray USING [PixelArray, FromAIS],
ImagerTransformation USING [Scale],
Labels,
PieViewers,
Real,
Rope,
Rules,
TIPUser,
ViewerOps,
ViewerClasses,
ViewerIO,
ViewerTools;
AISCopyImpl: CEDAR PROGRAM
IMPORTS AIS, Buttons, Commander, Containers, ExtraAIS, Icons, IO, FS, Imager, ImagerBackdoor, ImagerColorOperator, ImagerPixelArray, ImagerTransformation, PieViewers, Real, Rope, Rules, TIPUser, ViewerOps, ViewerIO, ViewerTools
= BEGIN
icon: Icons.IconFlavor ← Icons.NewIconFromFile[file: "AISCopy.icons", n: 1];
Parameter gathering and checking routines
Go: Buttons.ButtonProc = {
s: AISCopy.State ← NARROW[clientData];
fdIn,fdOut: AIS.FRef;
rIn: AIS.Raster;
rOut: AIS.Raster ← NEW[AIS.RasterPart];
wIn: ExtraAIS.VRef;
wOut: AIS.WRef;
inFn, outFn: Rope.ROPE;
emptyValue: CARDINAL;
s.parameterError ← FALSE;
inFn ← GetFileName[s, s.inFnViewer];
outFn ← GetFileName[s, s.outFnViewer];
s.imageX ← GetNumberFromViewer[s, s.leftViewer, -1024, 2048, "Left"];
s.imageY ← GetNumberFromViewer[s, s.topViewer, -1024, 2048, "Top"];
s.imageH ← GetNumberFromViewer[s, s.heightViewer, 0, 2048, "Scans"];
s.imageW ← GetNumberFromViewer[s, s.widthViewer, 0, 2048, "Pixels"];
s.copyH ← GetNumberFromViewer[s, s.oScansViewer, 0, 2048, "Output Scans"];
s.copyW ← GetNumberFromViewer[s, s.oPixelsViewer, 0, 2048, "Output Pixels"];
emptyValue ← GetNumberFromViewer[s, s.emptyViewer, 0, LAST[INTEGER], "Empty Value"];
s.scaleX ← s.copyW / Real.Float[s.imageW];
s.scaleY ← s.copyH / Real.Float[s.imageH];
s.abort ← FALSE;
IF s.parameterError THEN RETURN[];
TRUSTED {
fdIn ← AIS.OpenFile[name: inFn ! AIS.Error => {
IO.PutF[s.out, "\n%g does not exist", IO.rope[inFn]];
GOTO notThere;
}];
IO.PutF[s.out, "\nOpening %g", IO.rope[inFn]];
rIn ← AIS.ReadRaster[fdIn];
rOut^ ← rIn^;
rOut.scanCount ← s.copyH;
rOut.scanLength ← s.copyW;
IO.PutF[s.out, "\nCreating %g", IO.rope[outFn]];
fdOut ← AIS.CreateFile[name: outFn, raster: rOut];
wIn ← ExtraAIS.OpenWindow[fdIn, s.imageY, s.imageY+(s.imageH-1), s.imageX, s.imageX+(s.imageW-1), emptyValue];
wOut ← AIS.OpenWindow[fdOut];
IO.PutF[s.out, "\nProcessing %g", IO.rope[outFn]];
IF s.scaleY < 1.0 THEN ShrinkNoOfLines[s, wIn, wOut]
ELSE ExpandNoOfLines[s, wIn, wOut];
ExtraAIS.CloseWindow[wIn];
AIS.CloseWindow[wOut];
AIS.CloseFile[fdIn];
AIS.CloseFile[fdOut];
IO.PutF[s.out, "\nDone"];
IF ~s.abort THEN {
PieViewers.Set[s.pie, 0];
Init[outFn];
};
EXITS
notThere => NULL;
};
};
Abort: Buttons.ButtonProc = {
Sets a flag so scanning loops will terminate early
s: AISCopy.State ← NARROW[clientData];
IO.PutF[s.out,"\nABORT"];
s.abort ← TRUE;
};
Peek: Buttons.ButtonProc = {
s: AISCopy.State ← NARROW[clientData];
s.parameterError ← FALSE;
Show[s];
};
Show: PROC[s: AISCopy.State] = TRUSTED {
Loads a file into the AIS viewer - filename is expected to be correct!
f: Rope.ROPE ← GetFileName[s, s.inFnViewer];
fdIn: AIS.FRef;
rIn: AIS.Raster ← NEW[AIS.RasterPart];
IF s.parameterError THEN RETURN;
fdIn ← AIS.OpenFile[name: f ! FS.Error, AIS.Error => {
IO.PutF[s.out, "\n%g does not exist", IO.rope[f]];
GOTO notThere;
}];
IO.PutF[s.out, "\nOpening %g", IO.rope[f]];
rIn ← AIS.ReadRaster[fdIn];
s.copyH ← s.sourceH ← s.imageH ← rIn.scanCount;
s.copyW ← s.sourceW ← s.imageW ← rIn.scanLength;
s.imageX ← s.imageY ← 0;
AIS.CloseFile[fdIn];
RefreshParams[s];
s.container.name ← s.showFilename ← f;
ViewerOps.PaintViewer[s.container, caption];
ViewerOps.PaintViewer[s.AISViewer, client];
EXITS
notThere => NULL;
};
RefreshParams: PROC [s: AISCopy.State] = BEGIN
ViewerTools.SetContents[s.topViewer, IO.PutFR["%g", IO.card[s.imageY]]];
ViewerTools.SetContents[s.leftViewer, IO.PutFR["%g", IO.card[s.imageX]]];
ViewerTools.SetContents[s.heightViewer, IO.PutFR["%g", IO.card[s.imageH]]];
ViewerTools.SetContents[s.widthViewer, IO.PutFR["%g", IO.card[s.imageW]]];
ViewerTools.SetContents[s.oScansViewer, IO.PutFR["%g", IO.card[s.copyH]]];
ViewerTools.SetContents[s.oPixelsViewer, IO.PutFR["%g", IO.card[s.copyW]]];
ViewerTools.SetContents[s.oScaleP, IO.PutFR["%g", IO.real[s.scaleX]]];
ViewerTools.SetContents[s.oScaleS, IO.PutFR["%g", IO.real[s.scaleY]]];
END;
Select: Buttons.ButtonProc = {
Red bug: pending-delete select
Blue bug: make this parameter consistent with others
l: LIST OF REF ANYNARROW[clientData];
s: AISCopy.State ← NARROW[l.first];
v: ViewerClasses.Viewer ← NARROW[l.rest.first];
s.parameterError ← FALSE;
s.imageH ← GetNumberFromViewer[s, s.heightViewer, 0, 2048, "Scans"];
s.imageW ← GetNumberFromViewer[s, s.widthViewer, 0, 2048, "Pixels"];
s.scaleY ← GetRealFromViewer[s, s.oScaleS, 0, 10, "Scans scale"];
s.scaleX ← GetRealFromViewer[s, s.oScaleP, 0, 10, "Pixels scale"];
s.copyH ← GetNumberFromViewer[s, s.oScansViewer, 0, 32000, "Output Scans"];
s.copyW ← GetNumberFromViewer[s, s.oPixelsViewer, 0, 32000, "Output Pixels"];
IF ~s.parameterError AND mouseButton#red THEN
SELECT v FROM
s.heightViewer => {
s.imageH ← Real.RoundC[s.copyH / s.scaleY];
ViewerTools.SetContents[s.heightViewer, IO.PutFR["%g", IO.card[s.imageH]]];
};
s.widthViewer => {
s.imageW ← Real.RoundC[s.copyW / s.scaleX];
ViewerTools.SetContents[s.widthViewer, IO.PutFR["%g", IO.card[s.imageW]]];
};
s.oScansViewer => {
s.copyH ← Real.RoundC[s.imageH * s.scaleY];
ViewerTools.SetContents[s.oScansViewer, IO.PutFR["%g", IO.card[s.copyH]]];
};
s.oPixelsViewer => {
s.copyW ← Real.RoundC[s.imageW * s.scaleX];
ViewerTools.SetContents[s.oPixelsViewer, IO.PutFR["%g", IO.card[s.copyW]]];
};
s.oScaleP => {
s.scaleX ← s.copyW / Real.Float[s.imageW];
ViewerTools.SetContents[s.oScaleP, IO.PutFR["%f", IO.real[s.scaleX]]];
};
s.oScaleS => {
s.scaleY ← s.copyH / Real.Float[s.imageH];
ViewerTools.SetContents[s.oScaleS, IO.PutFR["%f", IO.real[s.scaleY]]];
};
ENDCASE;
ViewerTools.SetSelection[v];
};
Scale Manipulation
AvBuf: TYPE = REF AvBufRec;
AvBufRec: TYPE = ARRAY[0..2048] OF LONG CARDINAL;
IOBuffer: TYPE = REF IOBufferRec;
IOBufferRec: TYPE = PACKED ARRAY [0..2048) OF [0..255];
ShrinkNoOfLines: PUBLIC PROC[s: AISCopy.State, wIn: ExtraAIS.VRef, wOut: AIS.WRef] = {
iLineIx,oLineIx: CARDINAL ← 0;
avBuf: AvBuf ← NEW[AvBufRec];
ioBuffer: IOBuffer ← NEW[IOBufferRec];
ExpandNoOfPixels: PROC[s: AISCopy.State, avBuf: AvBuf, ioBuffer: IOBuffer] = BEGIN
Linearly interpolates pixels between pairs.
FOR inIx: CARDINAL IN [0..s.imageW] DO
outPrevCIx: INTEGER ← Real.FixI[(inIx-0.5)*s.scaleX];
outThisCIx: INTEGER ← Real.FixI[(inIx+0.5)*s.scaleX];
lb: CARDINAL ← MAX[0, outPrevCIx];
ub: CARDINAL ← MIN[outThisCIx, s.copyW];
inIxM1: CARDINAL = IF inIx = 0 THEN 0 ELSE inIx - 1;
FOR i: CARDINAL IN [lb..ub) DO
f: REAL ← (i - outPrevCIx) / Real.Float[(outThisCIx - outPrevCIx)];
r: REAL ← ioBuffer[inIxM1] + (ioBuffer[MIN[inIx, s.imageW-1]] - ioBuffer[inIxM1]) * f;
avBuf[i] ← avBuf[i] + Real.RoundC[r];
ENDLOOP;
ENDLOOP;
END;
ShrinkNoOfPixels: PROC[s: AISCopy.State, avBuf: AvBuf, ioBuffer: IOBuffer] = BEGIN
k: CARDINAL ← 0;
FOR i: CARDINAL IN [0..s.copyW) DO
noOfPixels: CARDINAL ← 0; -- counts number of pixels to be averaged
sum: LONG CARDINAL ← 0; -- where pixel sum is accumulated for averaging
-- compress the next few pixels into one
DO
sum ← sum + ioBuffer[k]; -- pick up an input pixel
k ← k + 1;
noOfPixels ← noOfPixels + 1; -- keep score of no. of inputs needed for this output
-- stop pixel averaging when next input would contribute to next output pixel
IF i+1 < k*s.scaleX THEN {
-- add the average of these pixels into line buffer
avBuf[i] ← avBuf[i] + Real.RoundC[sum / Real.Float[noOfPixels]];
EXIT;
};
ENDLOOP;
ENDLOOP;
END;
TRUSTED {
-- transfer input to output with compression
WHILE oLineIx < s.copyH DO
noOfLines: CARDINAL ← 0;
-- find first input line for this output line
iLineIx ← Real.FixC[oLineIx / s.scaleY];
-- zero the averaging buffer
FOR i: CARDINAL IN [0..s.copyW] DO
avBuf[i] ← 0;
ENDLOOP;
-- compress and add in the next few input lines
DO
-- read a line from input file
ExtraAIS.UnsafeReadLine[v: wIn, buffer: [length: s.imageW, addr: LOOPHOLE[ioBuffer]], line: iLineIx];
-- scale this input line
IF s.scaleX<=1.0 THEN ShrinkNoOfPixels[s, avBuf, ioBuffer]
ELSE ExpandNoOfPixels[s, avBuf, ioBuffer];
iLineIx ← iLineIx + 1;
noOfLines ← noOfLines + 1;
IF s.abort THEN RETURN;
-- stop line averaging when next input would contribute to next output line
-- "OR iLineIx=s.imageH" - avoids F.P. fuzz problems
IF oLineIx+1 <= iLineIx*s.scaleY OR iLineIx=s.imageH THEN EXIT;
ENDLOOP;
-- generate the 'average' as the working buffer is copied to the output buffer
FOR i: CARDINAL IN [0..s.copyW] DO
ioBuffer[i] ← Real.RoundC[avBuf[i] / Real.Float[noOfLines]];
ENDLOOP;
-- write out the output buffer
AIS.UnsafeWriteLine[w: wOut, buffer: [length: s.copyW, addr: LOOPHOLE[ioBuffer]], line: oLineIx];
-- update the pie chart to show that another output line has been generated
PieViewers.Set[s.pie, ((s.imageH-iLineIx)*100.0)/s.imageH];
-- move to the next output line
oLineIx ← oLineIx + 1;
ENDLOOP;
};
};
ExpandNoOfLines: PUBLIC PROC[s: AISCopy.State, wIn: ExtraAIS.VRef, wOut: AIS.WRef] = {
inBuffer: IOBuffer ← NEW[IOBufferRec];
oPrevBuffer: IOBuffer ← NEW[IOBufferRec];
oNextBuffer: IOBuffer ← NEW[IOBufferRec];
outBuffer: IOBuffer ← NEW[IOBufferRec];
temp: IOBuffer;
ExpandNoOfPixels: PROC[s: AISCopy.State, outBuf: IOBuffer, inBuf: IOBuffer] = {
Linearly interpolates pixels between pairs.
FOR inIx: CARDINAL IN [0..s.imageW] DO
outPrevCIx: INTEGER ← Real.RoundI[(inIx-0.5)*s.scaleX];
outThisCIx: INTEGER ← Real.RoundI[(inIx+0.5)*s.scaleX];
lb: CARDINAL ← MAX[0, outPrevCIx];
ub: CARDINAL ← MIN[outThisCIx, s.copyW];
inIxM1: CARDINAL = IF inIx = 0 THEN 0 ELSE inIx - 1;
FOR i: CARDINAL IN [lb..ub) DO
f: REAL ← (i - outPrevCIx) / Real.Float[(outThisCIx - outPrevCIx)];
r: REAL ← inBuf[inIxM1] + (inBuf[MIN[inIx, s.imageW-1]] - inBuf[inIxM1]) * f;
outBuf[i] ← Real.RoundC[r];
ENDLOOP;
ENDLOOP;
};
ShrinkNoOfPixels: PROC[s: AISCopy.State, outBuf: IOBuffer, inBuf: IOBuffer] = {
k: CARDINAL ← 0;
FOR i: CARDINAL IN [0..s.copyW) DO
noOfPixels: CARDINAL ← 0; -- counts number of pixels to be averaged
sum: LONG CARDINAL ← 0; -- where pixel sum is accumulated for averaging
-- compress the next few pixels into one
DO
sum ← sum + inBuf[k]; -- pick up an input pixel
k ← k + 1;
noOfPixels ← noOfPixels + 1; -- keep score of no. of inputs needed for this output
-- stop pixel averaging when next input would contribute to next output pixel
IF i+1 < k*s.scaleX THEN {
outBuf[i] ← Real.RoundC[sum / Real.Float[noOfPixels]];
EXIT;
};
ENDLOOP;
ENDLOOP;
};
TRUSTED {
-- read in the first input line and scale it
ExtraAIS.UnsafeReadLine[v: wIn, buffer: [length: s.imageW, addr: LOOPHOLE[inBuffer]], line: 0];
IF s.scaleX<=1.0 THEN ShrinkNoOfPixels[s, oNextBuffer, inBuffer]
ELSE ExpandNoOfPixels[s, oNextBuffer, inBuffer];
FOR iLineIx: CARDINAL IN [0..s.imageH] DO
-- calculate indices of first and last output lines
oPrevCIx: INTEGER ← Real.FixI[(iLineIx-0.5)*s.scaleY];
oNextCIx: INTEGER ← Real.FixI[(iLineIx+0.5)*s.scaleY];
lb: CARDINAL ← MAX[0, oPrevCIx];
ub: CARDINAL ← MIN[oNextCIx, s.copyH];
inIxM1: CARDINAL = IF iLineIx = 0 THEN 0 ELSE iLineIx - 1;
-- swap the buffers
temp ← oPrevBuffer; oPrevBuffer ← oNextBuffer; oNextBuffer ← temp;
-- read the next line into the buffer and process it
ExtraAIS.UnsafeReadLine[v: wIn, buffer: [length: s.imageW, addr: LOOPHOLE[inBuffer]], line: MIN[iLineIx, s.imageH-1]];
IF s.scaleX<=1.0 THEN ShrinkNoOfPixels[s, oNextBuffer, inBuffer]
ELSE ExpandNoOfPixels[s, oNextBuffer, inBuffer];
-- interpolate output lines
FOR i: CARDINAL IN [lb..ub) DO
f: REAL ← (i-oPrevCIx) / Real.Float[(oNextCIx - oPrevCIx)];
FOR j: CARDINAL IN [0..s.copyW) DO
outBuffer[j] ← Real.RoundC[oPrevBuffer[j] + (oNextBuffer[j] - oPrevBuffer[j]) * f];
ENDLOOP;
-- write out the new line
AIS.UnsafeWriteLine[w: wOut, buffer: [length: s.copyW, addr: LOOPHOLE[outBuffer]], line: i];
-- update the pie chart to show that another output line has been generated
PieViewers.Set[s.pie, ((s.imageH-iLineIx)*100.0)/s.imageH];
ENDLOOP;
ENDLOOP;
};
};
AIS displayer
GetNumberFromViewer: PUBLIC PROC[s: AISCopy.State, v: ViewerClasses.Viewer, lb,ub: INTEGER, er: Rope.ROPE] RETURNS[i: INTEGER] = {
Reads in a number from a text viewer and does a range check. Reports errors in the log viewer.
in: IO.STREAMIO.RIS[ViewerTools.GetContents[v]];
i ← lb;
i ← IO.GetInt[in
! IO.EndOfStream => {
IO.PutF[s.out, "\nSpecify %g please (%g..%g)", IO.rope[er], IO.card[lb], IO.card[ub]];
s.parameterError ← TRUE;
GOTO Croak;
}];
IF NOT (i IN [lb..ub]) THEN {
s.parameterError ← TRUE;
IO.PutF[s.out, "\n%g is not in the range %g..%g", IO.rope[er], IO.card[lb], IO.card[ub]];
};
EXITS
Croak => NULL;
};
GetRealFromViewer: PUBLIC PROC[s: AISCopy.State, v: ViewerClasses.Viewer, lb,ub: REAL, er: Rope.ROPE] RETURNS[r: REAL] = {
Reads in a number from a text viewer and does a range check. Reports errors in the log viewer.
in: IO.STREAMIO.RIS[ViewerTools.GetContents[v]];
r ← lb;
r ← IO.GetReal[in
! IO.EndOfStream => {
IO.PutF[s.out, "\nSpecify %g please (%g..%g)", IO.rope[er], IO.real[lb], IO.real[ub]];
s.parameterError ← TRUE;
GOTO Croak;
}];
IF NOT (r IN [lb..ub]) THEN {
s.parameterError ← TRUE;
IO.PutF[s.out, "\n%g is not in the range %g..%g", IO.rope[er], IO.real[lb], IO.real[ub]];
};
EXITS
Croak => NULL;
};
GetFileName: PUBLIC PROC[s: AISCopy.State, v: ViewerClasses.Viewer]
RETURNS[fileName: Rope.ROPE] = {
cp: FS.ComponentPositions;
[fileName, cp] ← FS.ExpandName[name: ViewerTools.GetContents[v],
wDir: ViewerTools.GetContents[s.wdViewer]
! FS.Error => {IO.PutF[s.out, "\nFS Error: %g", IO.rope[error.explanation]];
s.parameterError ← TRUE;
fileName ← NIL; GOTO Out;
}];
IF cp.base.length = 0 THEN {
IO.PutF[s.out, "\nSpecify a filename please"];
s.parameterError ← TRUE;
fileName ← NIL;
}
ELSE IF cp.ext.length # 3 OR
NOT Rope.Equal["ais", Rope.Substr[fileName, cp.ext.start, cp.ext.start+cp.ext.length], FALSE] THEN {
IO.PutF[s.out, "\nSpecify a filename with an ais extension please"];
s.parameterError ← TRUE;
fileName ← NIL;
}
ELSE IF cp.ext.length = 0 THEN fileName ← Rope.Concat[fileName, ".ais"];
EXITS Out => RETURN [NIL];
};
FeedbackParams: PROC[s: AISCopy.State] = {
ViewerTools.SetContents[s.widthViewer, IO.PutFR["%g", IO.card[s.imageW]]];
ViewerTools.SetContents[s.heightViewer, IO.PutFR["%g", IO.card[s.imageH]]];
ViewerTools.SetContents[s.oPixelsViewer, IO.PutFR["%g", IO.card[s.imageW]]];
ViewerTools.SetContents[s.oScansViewer, IO.PutFR["%g", IO.card[s.imageH]]];
};
AISNotify: ViewerClasses.NotifyProc = TRUSTED {
-- Handle mouse inputs for creating cropping box
s: AISCopy.State ← NARROW[self.data];
SELECT input.first FROM
$SetTopLeft =>
WITH input.rest.first SELECT FROM
xy: TIPUser.TIPScreenCoords => {
s.imageX ← Real.RoundC[MAX[MIN[(s.x1 ← xy.mouseX) / s.scale, 2047], 0]];
s.imageY ← Real.RoundC[MAX[MIN[s.sourceH - (s.y1 ← xy.mouseY) / s.scale, 2047], 0]]; -- scan 0 is at the top
s.imageW ← s.imageH ← 0;
s.x1 ← MAX[MIN[s.x1, (2047-s.imageX)*s.scale], 0];
s.y1 ← MAX[MIN[s.y1, (2047-s.imageY)*s.scale], 0];
ViewerTools.SetContents[s.topViewer, IO.PutFR["%g", IO.card[s.imageY]]];
ViewerTools.SetContents[s.leftViewer, IO.PutFR["%g", IO.card[s.imageX]]];
s.x2 ← s.x1+1; s.y2 ← s.y1+1;
FeedbackParams[s];
ViewerOps.PaintViewer[s.AISViewer, client, FALSE, $XORBOX];
};
ENDCASE;
$Adjust =>
WITH input.rest.first SELECT FROM
xy: TIPUser.TIPScreenCoords => {
ViewerOps.PaintViewer[s.AISViewer, client, FALSE, $XORBOX];
s.x2 ← xy.mouseX;
s.y2 ← xy.mouseY;
s.x2 ← MAX[MIN[s.x2, (2047-s.imageX)*s.scale], 0];
s.y2 ← MAX[MIN[s.y2, (2047-s.imageY)*s.scale], 0];
s.imageW ← Real.RoundC[ABS[(s.x2+1-s.x1)] / s.scale];
s.imageW ← MIN[MAX[2048-s.imageX, 0], s.imageW];
s.imageH ← Real.RoundC[ABS[(s.y1+1-s.y2)] / s.scale];
s.imageH ← MIN[MAX[2048-s.imageY, 0], s.imageH];
FeedbackParams[s];
ViewerOps.PaintViewer[s.AISViewer, client, FALSE, $XORBOX];
};
ENDCASE;
$SetBottomRight =>
WITH input.rest.first SELECT FROM
xy: TIPUser.TIPScreenCoords => {
ViewerOps.PaintViewer[s.AISViewer, client, FALSE, $XORBOX];
s.x2 ← xy.mouseX;
s.y2 ← xy.mouseY;
s.x2 ← MAX[MIN[s.x2, (2047-s.imageX)*s.scale], 0];
s.y2 ← MAX[MIN[s.y2, (2047-s.imageY)*s.scale], 0];
s.imageW ← Real.RoundC[ABS[(s.x2+1-s.x1)] / s.scale];
s.imageW ← MIN[MAX[2048-s.imageX, 0], s.imageW];
s.imageH ← Real.RoundC[ABS[(s.y1+1-s.y2)] / s.scale];
s.imageH ← MIN[MAX[2048-s.imageY, 0], s.imageH];
FeedbackParams[s];
-- if the user creates the box in the wrong direction then fix up the numbers
IF s.x2<s.x1 THEN {
s.imageX ← s.imageX - s.imageW;
ViewerTools.SetContents[s.leftViewer, IO.PutFR["%g", IO.card[s.imageX]]];
};
IF s.y2>s.y1 THEN {
s.imageY ← s.imageY + s.imageH;
ViewerTools.SetContents[s.topViewer, IO.PutFR["%g", IO.card[s.imageY]]];
}
};
ENDCASE;
ENDCASE => ERROR;
};
AISPaint: ViewerClasses.PaintProc = TRUSTED {
s: AISCopy.State ← NARROW[self.data];
IF whatChanged=$XORBOX THEN {
Provide interactive feed back for cropping process
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.MaskBox[context, [s.x1, s.y1, s.x2, s.y2]];
}
ELSE {
Copy the image for the available space
sx,sy: REAL;
box: Imager.Rectangle ← ImagerBackdoor.GetBounds[context];
fileExists: BOOLEANTRUE;
pixelArray: ImagerPixelArray.PixelArray;
sx ← box.w / s.sourceW;
sy ← box.h / s.sourceH;
s.scale ← MIN[sx,sy];
pixelArray ← ImagerPixelArray.FromAIS[s.showFilename ! FS.Error => { fileExists ← FALSE; CONTINUE; }];
IF fileExists THEN{
Imager.Scale2T[context, [s.scale, s.scale]];
Imager.SetSampledColor[context: context, m: ImagerTransformation.Scale[1.0], pa: pixelArray, colorOperator: ImagerColorOperator.GrayLinearColorModel[sWhite~255, sBlack~0]];
Imager.MaskBox[context, [0,0, s.sourceW, s.sourceH]];
}
};
};
Initialization
Init: PROCEDURE [f: Rope.ROPE, wd: Rope.ROPENIL] = {
Rule: PROC [wx, wy, ww, wh: INTEGER] ~ {
[] ← Rules.Create[info: [wx: wx, wy: wy, ww: ww, wh: wh, parent: s.container]];
};
Button: PROC [name: Rope.ROPE, wx, wy: INTEGER, proc: Buttons.ButtonProc, clientData: REF] ~ {
[] ← Buttons.Create[info: [name: name, wx: wx, wy: wy, parent: s.container], proc: proc, clientData: clientData];
};
SelectButton: PROC [name: Rope.ROPE, wx, wy: INTEGER, v: ViewerClasses.Viewer] ~ {
[] ← Buttons.Create[info: [name: name, wx: wx, wy: wy, parent: s.container], proc: Select, clientData: LIST[s, v]];
};
SelectViewer: PROC [wx, wy, ww, wh: INTEGER, data: Rope.ROPENIL]
RETURNS [ViewerClasses.Viewer] ~ {
RETURN [ViewerTools.MakeNewTextViewer[info: [wx: wx, wy: wy, ww: ww, wh: wh, parent: s.container, data: data, border: FALSE, scrollable: TRUE]]]
};
s: AISCopy.State ← NEW[AISCopy.StateRec];
s.container ← ViewerOps.CreateViewer[flavor: $Container, info: [name: "Copy", scrollable: FALSE, icon: icon]];
-- Divide image from controls
Rule[wx: 0, wy: 112, ww: 1000, wh: 2];
-- Divide Input parameters from Output parameters
Rule[wx: 255, wy: 22, ww: 2, wh: 68];
-- Divide Output parameters from Command buttons
Rule[wx: 505, wy: 22, ww: 2, wh: 68];
-- Divide parameters from feedback
Rule[wx: 0, wy: 90, ww: 1000, wh: 2];
-- Divide working directory from parameters
Rule[wx: 0, wy: 22, ww: 1000, wh: 2];
-- Image viewer
s.AISViewer ← ViewerOps.CreateViewer[flavor: $AISCopy, info: [name: "AIS file", wx: 0, wy: 114, ww: 0, wh: 0, parent: s.container, data: s]];
Containers.ChildYBound[s.container, s.AISViewer];
Containers.ChildXBound[s.container, s.AISViewer];
-- working directory button
IF wd.IsEmpty AND NOT f.IsEmpty THEN {
fullFName: Rope.ROPE;
cp: FS.ComponentPositions;
[fullFName, cp] ← FS.ExpandName[f];
wd ← Rope.Substr[fullFName, 0, cp.base.start];
f ← Rope.Substr[fullFName, cp.base.start, cp.ver.start+cp.ver.length];
};
s.wdViewer ← ViewerTools.MakeNewTextViewer[info: [wx: 53, wy: 3, ww: 544, wh: 15, parent: s.container, border: FALSE, scrollable: TRUE, data: wd]];
Button[name: "WD: ", wx: 10, wy: 3, proc: SelectWD, clientData: s.wdViewer];
-- Input controls
s.inFnViewer ← SelectViewer[wx: 112, wy: 32, ww: 130, wh: 15];
SelectButton[name: "Input Filename: ", wx: 10, wy: 32, v: s.inFnViewer];
s.topViewer ← SelectViewer[wx: 42, wy: 52, ww: 50, wh: 15, data: "0"];
SelectButton[name: "Top:", wx: 10, wy: 52, v: s.topViewer];
s.leftViewer ← SelectViewer[wx: 42, wy: 72, ww: 50, wh: 15, data: "0"];
SelectButton[name: "Left:", wx: 10, wy: 72, v: s.leftViewer];
s.heightViewer ← SelectViewer[wx: 135, wy: 52, ww: 50, wh: 15, data: "2048"];
SelectButton[name: "Scans:", wx: 94, wy: 52, v: s.heightViewer];
s.widthViewer ← SelectViewer[wx: 135, wy: 72, ww: 50, wh: 15, data: "2048"];
SelectButton[name: "Pixels:", wx: 94, wy: 72, v: s.widthViewer];
Button[name: "PEEK", wx: 205, wy: 52, proc: Peek, clientData: s];
s.emptyViewer ← SelectViewer[wx: 201, wy: 72, ww: 54, wh: 15, data: "0"];
SelectButton[name: "e:", wx: 185, wy: 72, v: s.emptyViewer];
-- Output parameters
s.outFnViewer ← SelectViewer[wx: 371, wy: 32, ww: 130, wh: 15];
SelectButton[name: "Output Filename: ", wx: 260, wy: 32, v: s.outFnViewer];
s.oScansViewer ← SelectViewer[wx: 302, wy: 52, ww: 50, wh: 15, data: "2048"];
SelectButton[name: "Scans:", wx: 260, wy: 52, v: s.oScansViewer];
s.oPixelsViewer ← SelectViewer[wx: 302, wy: 72, ww: 50, wh: 15, data: "2048"];
SelectButton[name: "Pixels:", wx: 260, wy: 72, v: s.oPixelsViewer];
s.oScaleS ← SelectViewer[wx: 400, wy: 52, ww: 50, wh: 15, data: "1.0"];
SelectButton[name: "Scale:", wx: 360, wy: 52, v: s.oScaleS];
s.oScaleP ← SelectViewer[wx: 400, wy: 72, ww: 50, wh: 15, data: "1.0"];
SelectButton[name: "Scale:", wx: 360, wy: 72, v: s.oScaleP];
-- Command buttons
Button[name: "GO", wx: 520, wy: 32, proc: Go, clientData: s];
Button[name: "ABORT", wx: 520, wy: 52, proc: Abort, clientData: s];
-- Feedback instruments
s.logViewer ← ViewerOps.CreateViewer[flavor: $TypeScript, info: [wx: 30, wy: 94, ww: 560, wh: 15, parent: s.container]];
[ , s.out] ← ViewerIO.CreateViewerStreams[viewer: s.logViewer, name: NIL];
s.pie ← PieViewers.Create[parent: s.container, x: 10, y: 94];
IF f#NIL THEN {
ViewerTools.SetContents[s.inFnViewer, f];
Show[s];
};
};
SelectWD: Buttons.ButtonProc ~ {
wdViewer: ViewerClasses.Viewer ← NARROW[clientData];
SELECT mouseButton FROM
red => {
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
newWD: Rope.ROPENIL;
IF selectedViewer # NIL THEN newWD ← ViewerTools.GetSelectionContents[];
IF Rope.Size[newWD] <= 1 THEN {
get wd from working directory of name of viewer
SELECT selectedViewer.class.flavor FROM
$Text => {
newWD from file name of viewer
IF NOT Rope.IsEmpty[selectedViewer.file] THEN {
fullFName: Rope.ROPE;
cp: FS.ComponentPositions;
[fullFName, cp] ← FS.ExpandName[selectedViewer.file];
newWD ← Rope.Substr[fullFName, 0, cp.base.start];
}
ELSE newWD ← NIL;
};
$Typescript => {
newWD from CommandTool; lots of heuristics here my friend!!!
IF Rope.Equal["CommandTool", Rope.Substr[selectedViewer.name, 0, 11]] THEN
newWD ← Rope.Substr[selectedViewer.name, 18]
ELSE newWD ← NIL;
};
ENDCASE => newWD ← NIL;
};
IF NOT newWD.IsEmpty THEN ViewerTools.SetContents[wdViewer, newWD];
};
yellow => NULL;
blue => ViewerTools.SetContents[wdViewer, ""];
ENDCASE;
ViewerTools.SetSelection[wdViewer];
};
StartNewCopyProcess: Commander.CommandProc = {
-- creates a new incarnation of the scanner program
f: Rope.ROPE;
empty: BOOLEANFALSE;
p: PROCESS;
wd: Rope.ROPE ← Commander.PrependWorkingDir["a"];
wd ← Rope.Substr[wd, 0, Rope.Size[wd]-1];
f ← IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc
! IO.EndOfStream => {
empty ← TRUE;
CONTINUE;
}].token;
p ← FORK Init[IF empty OR Rope.Length[f]<1 THEN NIL ELSE f, wd];
};
AISCopyClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec];
AISCopyClass.paint ← AISPaint;
AISCopyClass.notify ← AISNotify;
AISCopyClass.tipTable ← TIPUser.InstantiateNewTIPTable["AISCopy.tip"];
ViewerOps.RegisterViewerClass[$AISCopy, AISCopyClass];
Commander.Register[key: "AISCopy", proc: StartNewCopyProcess, doc: "AIS copying program"];
END.