<> <> <> 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: BOOLEAN _ TRUE] ~ { text: REF TEXT _ NEW[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: "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: BOOLEAN _ TRUE, 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: ROPE _ NIL] = { 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.STREAM _ IO.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.