StippleEditImpl.mesa
Copyright (C) 1984, Xerox Corporation. All rights reserved.
Michael Plass, September 3, 1985 12:38:43 pm PDT
DIRECTORY
AIS USING [Buffer, CloseFile, CloseWindow, CreateFile, Error, FRef, OpenFile, OpenWindow, Raster, RasterPart, ReadRaster, UnsafeReadLine, UnsafeWriteLine, WRef, WriteComment],
Basics USING [BITSHIFT],
BitmapEdit USING [CreateBitmapViewer, GetBitmap, SetBitmap],
Commander USING [CommandProc, Register],
Convert USING [AppendInt, AppendReal, AppendRope],
FS USING [ComponentPositions, Error, ExpandName, FileInfo, SetKeep],
ImagerPixelMap USING [Clear, Clip, Create, CreateTile, Fill, PixelMap, Reflect, Rotate, Transfer, TransferTile],
IO USING [CharClass, EndOfStream, GetTokenRope, PutChar, PutRope, RIS, STREAM],
Menus USING [AppendMenuEntry, ClickProc, CreateEntry, CreateMenu, Menu],
MessageWindow USING [Append, Blink],
Rope USING [Concat, FromRefText, ROPE, Size, Substr],
ViewerClasses USING [SaveAborted, SaveProc, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [PaintViewer, SaveViewer],
ViewerTools USING [GetSelectedViewer];
StippleEditImpl: CEDAR PROGRAM
IMPORTS AIS, Basics, BitmapEdit, Commander, Convert, FS, ImagerPixelMap, IO, Menus, MessageWindow, Rope, ViewerClasses, ViewerOps, ViewerTools
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
GetSelection: PROC RETURNS [selectionContents: ROPE] ~ {
selectedViewer: ViewerClasses.Viewer ← ViewerTools.GetSelectedViewer[];
IF selectedViewer = NIL OR selectedViewer.class.get = NIL THEN RETURN [NIL];
WITH selectedViewer.class.get[selectedViewer, $SelChars] SELECT FROM
rope: ROPE => selectionContents ← rope;
ENDCASE => selectionContents ← NIL;
};
ComputeCaption: PROC [viewer: ViewerClasses.Viewer, paint: BOOLEANTRUE] ~ {
text: REF TEXTNEW[TEXT[200]];
PutRope: PROC [rope: ROPE] ~ {text ← Convert.AppendRope[text, rope, FALSE]};
PutInt: PROC [int: INT] ~ {text ← Convert.AppendInt[text, int]};
PutReal: PROC [real: REAL] ~ {text ← Convert.AppendReal[text, real]};
pixelMap: PixelMap ← BitmapEdit.GetBitmap[viewer].pixelMap;
PutRope[viewer.file];
PutRope[" ("];
PutInt[pixelMap.sSize];
PutRope[" lines "];
PutInt[pixelMap.fSize];
PutRope[" dots)"];
viewer.name ← Rope.FromRefText[text];
IF paint THEN ViewerOps.PaintViewer[viewer, caption];
text ← NIL;
};
Create: PUBLIC PROC [aisFileName: ROPE] RETURNS [viewer: ViewerClasses.Viewer] ~ {
menu: Menus.Menu ← Menus.CreateMenu[];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Undo",
proc: UndoButton,
documentation: "Undo the last edit"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Reset",
proc: ResetButton,
documentation: "Get the bits from the file again"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Get",
proc: GetButton,
documentation: "Load the bits from the AIS file named by the selection"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Store",
proc: StoreButton,
documentation: "Store the bits into the AIS file named by the selection"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Save",
proc: SaveButton,
documentation: "Save the bits into the AIS file named by the caption"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Bounds",
proc: BoundsButton,
documentation: "Set the boundaries according to the crosshairs"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Fill",
proc: FillButton,
documentation: "Fill the rectangle selected by the crosshairs with black/invert/white"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Tile",
proc: TileButton,
documentation: "Tile-replace/xor/or the rectangle selected by the crosshairs from the AIS file named by the selection"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Rotate",
proc: RotateButton,
documentation: "Rotates the bitmap 90 degrees"
]
];
Menus.AppendMenuEntry[
menu: menu,
entry: Menus.CreateEntry[
name: "Flip",
proc: FlipButton,
documentation: "Flips the bitmap horizontally"
]
];
viewer ← BitmapEdit.CreateBitmapViewer[4, 4, [name: "[No file]", file: NIL, menu: menu]];
viewer.class ← SmashClass[viewer.class];
IF Rope.Size[aisFileName] # 0 THEN Get[viewer, aisFileName];
ViewerOps.PaintViewer[viewer, all];
};
PixelMapFromAIS: PUBLIC PROC [aisName: ROPE] RETURNS [pixelMap: ImagerPixelMap.PixelMap, raster: AIS.Raster] ~ TRUSTED {
ais: AIS.FRef ← AIS.OpenFile[aisName];
window: AIS.WRef ← AIS.OpenWindow[ais];
lgBitsPerPixel: [0..4] ← SELECT (raster ← AIS.ReadRaster[ais]).bitsPerPixel FROM
0, 1 => 0,
2 => 1,
4 => 2,
8 => 3,
16 => 4,
ENDCASE => ERROR;
lineMap: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[lgBitsPerPixel, [0, 0, 1, raster.scanLength]];
lineBufferDesc: AIS.Buffer ← [length: lineMap.refRep.words, addr: lineMap.refRep.pointer];
pixelMap ← ImagerPixelMap.Create[lgBitsPerPixel, [0, 0, raster.scanCount, raster.scanLength]];
FOR i: NAT IN [0..raster.scanCount) DO
AIS.UnsafeReadLine[window, lineBufferDesc, i];
pixelMap.Transfer[lineMap];
lineMap.sOrigin ← lineMap.sOrigin + 1;
ENDLOOP;
AIS.CloseWindow[window];
AIS.CloseFile[ais];
};
Complain: ERROR [msg: ROPE, hard: BOOL] ~ CODE;
Complainer: PROC [msg: ROPE] ~ {
MessageWindow.Append[msg, TRUE];
MessageWindow.Blink[];
};
InheritWD: PROC [fileName, parent: ROPE] RETURNS [ROPE] ~ {
IF Rope.Size[parent] # 0 THEN {
cp: FS.ComponentPositions;
[parent, cp] ← FS.ExpandName[parent !
FS.Error => ERROR Complain[msg: error.explanation, hard: FALSE]
];
parent ← parent.Substr[0, cp.base.start];
}
ELSE parent ← NIL;
fileName ← FS.ExpandName[fileName, parent !
FS.Error => ERROR Complain[msg: error.explanation, hard: FALSE]
].fullFName;
RETURN [fileName]
};
GetPixelMap: PROC [aisFileName: ROPE] RETURNS [pixelMap: PixelMap] ~ {
raster: AIS.Raster;
[pixelMap, raster] ← PixelMapFromAIS[aisFileName !
FS.Error => ERROR Complain[msg: error.explanation, hard: error.group=bug OR error.group=client OR error.code=$hardware];
AIS.Error => ERROR Complain[msg: aisFileName.Concat[" not a valid AIS file."], hard: type#invalidFile];
];
IF pixelMap.refRep.lgBitsPerPixel # 0 THEN ERROR Complain[msg: "AIS file is not a bitmap", hard: FALSE];
};
Get: PROC [viewer: ViewerClasses.Viewer, aisFileName: ROPE] ~ {
pixelMap: PixelMap;
w: ROPE ← viewer.file;
aisFileName ← InheritWD[aisFileName, viewer.file];
pixelMap ← GetPixelMap[aisFileName];
viewer.file ← aisFileName;
BitmapEdit.SetBitmap[viewer, pixelMap, 0, 0];
ComputeCaption[viewer];
};
StorePixelMap: PROC [aisFileName: ROPE, source: ImagerPixelMap.PixelMap, bitmap: BOOLEANTRUE, comment: ROPE ← NIL] ~ TRUSTED {
output: AIS.FRef ← AIS.CreateFile[
name: aisFileName,
raster: NEW[AIS.RasterPart ← [
scanCount: source.sSize,
scanLength: source.fSize,
scanMode: rd,
bitsPerPixel: IF source.refRep.lgBitsPerPixel = 0 AND bitmap THEN 0 ELSE Basics.BITSHIFT[1, source.refRep.lgBitsPerPixel],
linesPerBlock: -1,
paddingPerBlock: 65535
]] !
FS.Error => ERROR Complain[msg: error.explanation, hard: error.group=bug OR error.group=client OR error.code=$hardware];
AIS.Error => ERROR Complain[msg: aisFileName.Concat[" AIS Error"], hard: TRUE];
];
outputWindow: AIS.WRef ← AIS.OpenWindow[output];
lineMap: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[source.refRep.lgBitsPerPixel, [source.sOrigin+source.sMin, source.fOrigin+source.fMin, 1, source.fSize]];
lineBufferDesc: AIS.Buffer ← [length: lineMap.refRep.words, addr: lineMap.refRep.pointer];
AIS.WriteComment[output, comment];
FOR i: NAT IN [0..source.sSize) DO
lineMap.Clear;
lineMap.Transfer[source];
lineMap.sOrigin ← lineMap.sOrigin + 1;
AIS.UnsafeWriteLine[outputWindow, lineBufferDesc, i];
ENDLOOP;
AIS.CloseFile[output];
};
keep: CARDINAL ← 3;
Save: ViewerClasses.SaveProc ~ {
name: ROPE ← self.file;
pixelMap: PixelMap;
sWidth, fWidth: INTEGER;
IF Rope.Size[name] # 0 THEN {
cp: FS.ComponentPositions;
[name, cp] ← FS.ExpandName[name];
name ← name.Substr[0, cp.ext.start+cp.ext.length];
};
FS.SetKeep[name, keep ! FS.Error => CONTINUE];
[pixelMap, sWidth, fWidth] ← BitmapEdit.GetBitmap[self];
StorePixelMap[name, pixelMap, TRUE, NIL ! Complain => {IF NOT hard THEN ERROR ViewerClasses.SaveAborted[msg]}];
self.file ← FS.FileInfo[name].fullFName;
ComputeCaption[self];
};
GetButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
rope: ROPE ← GetSelection[];
IF Rope.Size[rope] = 0 THEN Complainer["Please select an AIS file name."]
ELSE Get[viewer, rope ! Complain => {Complainer[msg]; IF NOT hard THEN CONTINUE}];
};
StoreButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
rope: ROPE ← GetSelection[];
oldFile: ROPE ← viewer.file;
viewer.file ← InheritWD[rope, oldFile ! Complain => {Complainer[msg]; IF NOT hard THEN GOTO Quit}];
ViewerOps.SaveViewer[viewer ! UNWIND => viewer.file ← oldFile];
EXITS Quit => NULL;
};
SaveButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
ViewerOps.SaveViewer[viewer];
};
UndoButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
};
ResetButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
IF viewer.file # NIL THEN Get[viewer, viewer.file ! Complain => {Complainer[msg]; IF NOT hard THEN CONTINUE}];
};
BoundsButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
pixelMap, new: PixelMap;
sWidth, fWidth: INTEGER;
sMin, fMin, sMax, fMax: INTEGER;
[pixelMap, sWidth, fWidth] ← BitmapEdit.GetBitmap[viewer];
sMin ← MIN[sWidth, 0];
fMin ← MIN[fWidth, 0];
sMax ← MAX[sWidth, 0];
fMax ← MAX[fWidth, 0];
new ← ImagerPixelMap.Create[0, [sMin, fMin, sMax-sMin, fMax-fMin]];
ImagerPixelMap.Clear[new];
ImagerPixelMap.Transfer[new, pixelMap];
BitmapEdit.SetBitmap[viewer, new, sWidth, fWidth];
ComputeCaption[viewer];
};
FillButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
pixelMap: PixelMap;
sWidth, fWidth: INTEGER;
sMin, fMin, sMax, fMax: INTEGER;
[pixelMap, sWidth, fWidth] ← BitmapEdit.GetBitmap[viewer];
sMin ← MIN[sWidth, 0];
fMin ← MIN[fWidth, 0];
sMax ← MAX[sWidth, 0];
fMax ← MAX[fWidth, 0];
ImagerPixelMap.Fill[pixelMap, [sMin, fMin, sMax-sMin, fMax-fMin], IF mouseButton = blue THEN 0 ELSE 1,
SELECT mouseButton FROM
red => [null, null],
yellow => [xor, null],
blue => [null, null],
ENDCASE => ERROR
];
BitmapEdit.SetBitmap[viewer, pixelMap, sWidth, fWidth];
};
TileButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
rope: ROPE ← GetSelection[];
pixelMap, source: PixelMap;
sWidth, fWidth: INTEGER;
sMin, fMin, sMax, fMax: INTEGER;
IF Rope.Size[rope] = 0 THEN {Complainer["Please select an AIS file name."]; RETURN};
BEGIN ENABLE Complain => {Complainer[msg]; IF NOT hard THEN GOTO Quit};
source ← GetPixelMap[InheritWD[rope, viewer.file]];
END;
[pixelMap, sWidth, fWidth] ← BitmapEdit.GetBitmap[viewer];
sMin ← MIN[sWidth, 0];
fMin ← MIN[fWidth, 0];
sMax ← MAX[sWidth, 0];
fMax ← MAX[fWidth, 0];
ImagerPixelMap.TransferTile[
dest: ImagerPixelMap.Clip[pixelMap, [sMin, fMin, sMax-sMin, fMax-fMin]],
tile: ImagerPixelMap.CreateTile[source],
function: (
SELECT mouseButton FROM
red => [null, null],
yellow => [xor, null],
blue => [or, null],
ENDCASE => ERROR
)
];
BitmapEdit.SetBitmap[viewer, pixelMap, sWidth, fWidth];
EXITS Quit => NULL;
};
RotateButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
pixelMap: PixelMap;
sWidth, fWidth: INTEGER;
[pixelMap, sWidth, fWidth] ← BitmapEdit.GetBitmap[viewer];
pixelMap ← ImagerPixelMap.Rotate[pixelMap];
BitmapEdit.SetBitmap[viewer, pixelMap, fWidth, -sWidth];
ComputeCaption[viewer];
};
FlipButton: Menus.ClickProc ~ {
viewer: ViewerClasses.Viewer ← NARROW[parent];
pixelMap: PixelMap;
sWidth, fWidth: INTEGER;
[pixelMap, sWidth, fWidth] ← BitmapEdit.GetBitmap[viewer];
pixelMap ← ImagerPixelMap.Reflect[pixelMap];
BitmapEdit.SetBitmap[viewer, pixelMap, -sWidth, fWidth];
ComputeCaption[viewer];
};
GetToken: PROC [stream: IO.STREAM] RETURNS [token: ROPENIL] = {
token ← stream.GetTokenRope[Break ! IO.EndOfStream => CONTINUE].token;
};
Break: PROC [char: CHAR] RETURNS [IO.CharClass] = {
IF char = '← OR char = '; THEN RETURN [break];
IF char = ' OR char = '  OR char = ', OR char = '\n THEN RETURN [sepr];
RETURN [other];
};
StippleEditCommand: Commander.CommandProc ~ {
stream: IO.STREAMIO.RIS[cmd.commandLine];
name: ROPE ← GetToken[stream];
[] ← Create[name ! Complain => {
cmd.out.PutRope[msg];
cmd.out.PutChar['\n];
IF NOT hard THEN CONTINUE;
}];
};
SmashClass: PROC [class: ViewerClasses.ViewerClass] RETURNS [new: ViewerClasses.ViewerClass] ~ {
new ← NEW[ViewerClasses.ViewerClassRec ← class^];
new.save ← Save;
};
Commander.Register["StippleEdit", StippleEditCommand, "Edit the named AIS bitmap file."];
END.