DIRECTORY ColorEditViewers, ColorEditViewersPrivate, AdjustColorStd USING [CurrentTRC, NormalizeAdjustments, TRC], Atom USING [GetPName], ColorDisplay USING [GetColorDisplayStatus], ColorEditOps USING [ApplyLog, LogApply, ReadExtentOfMask, SaveExtentOfMask], Commander USING [CommandProc, Register], CommandTool USING [CurrentWorkingDirectory, ParseToList], Cursors USING [CursorHandle, CursorInfo, CursorRec, CursorType, NewCursor, SetCursor], FS USING [Error, FileInfo, StreamOpen], Icons USING [IconFlavor], ImagerPixelMap USING [DeviceRectangle, Intersect], ImagerTransformation USING [Cat, Concat, Create, Invert, Scale, Transform, Transformation, Translate], InputFocus USING [GetInputFocus, SetInputFocus], Intime USING [EventTime, ReadEventTime], IO USING [Close, GetChar, EndOfStream, PutChar, PutRope, ROS, RopeFromROS, STREAM], MessageWindow USING [Append], PixelGraphics USING [InterpolateLines, Point], PopUpMenu USING [RequestSelection], Process USING [Detach], Real USING [RoundC, RoundI], RealOps USING [RoundLI, RoundI], Rope USING [Cat, Concat, ROPE], SampleArrayDisplay USING [BuildSampler, Display, Screen, ViewerInformation], SampleArrayIcons USING [NewIcon], SampleArrays USING [FileNameRoot, FromAIS, GetSample, Join3AIS, SampleArray, Save], SampleMapOps USING [Create, Clear, CVEC, GetSample, PutSample, SampleMap], SelectRegions USING [ApplyTRC, Differences, DiscriminateColor, Fill, FillRectangle, Intersect, MatchColor, MatchColorFill, nullRectangle, Operation, PaintBrush, PaintPixel, Rectangle, RGB, TRCSequence, TRCSequenceRep], TajoMenus USING [CreateTajoSystem, Select, TajoSystem], Terminal USING [Current, SetColorCursorPresentation, Virtual], TIPTables USING [TIPTime], TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPScreenCoordsRec], Vector2 USING [VEC], ViewerClasses USING [Column, HScrollProc, NotifyProc, PaintProc, ScrollProc, Viewer, ViewerClass, ViewerClassRec, ViewerRec], ViewerOps; ColorEditViewersImpl: CEDAR MONITOR IMPORTS AdjustColorStd, Atom, ColorDisplay, ColorEditOps, Commander, CommandTool, Cursors, FS, ImagerPixelMap, ImagerTransformation, InputFocus, Intime, IO, MessageWindow, PixelGraphics, PopUpMenu, Process, Real, RealOps, Rope, SampleArrayDisplay, SampleArrayIcons, SampleArrays, SampleMapOps, SelectRegions, TajoMenus, Terminal, TIPUser, ViewerOps EXPORTS ColorEditViewers ~ BEGIN OPEN ColorEditViewers, ColorEditViewersPrivate; ROPE: TYPE ~ Rope.ROPE; TRC: TYPE ~ AdjustColorStd.TRC; VEC: TYPE ~ Vector2.VEC; Viewer: TYPE ~ ViewerClasses.Viewer; Separation: TYPE ~ {red, green, blue}; --Color separations ColorEditViewerData: TYPE ~ REF ColorEditViewerDataRec; ColorEditViewerDataRec: PUBLIC TYPE ~ ColorEditViewersPrivate.ColorEditViewerDataRec; Quit: SIGNAL ~ CODE; term: Terminal.Virtual ~ Terminal.Current[]; op: ARRAY Button OF Operation; CantFindFile: PUBLIC ERROR ~ CODE; Create: PUBLIC PROCEDURE [info: ViewerClasses.ViewerRec _ [], clientData: REF ANY _ NIL] RETURNS [aisViewer: ColorEditViewer] ~ { info.scrollable _ info.hscrollable _ TRUE; [info.data, info.name, info.icon] _ Rope2ColorEditViewerData[info.name, clientData ! CantFindFile => { MessageWindow.Append[Rope.Concat[info.name, " not found."], TRUE]; GOTO Failed; } ]; aisViewer _ ViewerOps.CreateViewer[flavor: $ColorEditViewer, info: info]; [] _ AISVScroll[self: aisViewer, op: up, amount: 0, shift: TRUE, control: TRUE]; EXITS Failed => NULL; }; CurrentViewer: PUBLIC ENTRY PROC RETURNS [ColorEditViewer] ~ { ENABLE UNWIND => NULL; v: ColorEditViewer ~ InputFocus.GetInputFocus[].owner; RETURN [IF v=NIL OR v.class.flavor#$ColorEditViewer THEN NIL ELSE v]; }; SAFileInfo: TYPE ~ RECORD [sa: SampleArrays.SampleArray, name: ROPE]; nullSAInfo: SAFileInfo ~ [NIL, NIL]; Rope2ColorEditViewerData: PROC [file: ROPE, clientData: REF _ NIL] RETURNS [aisViewerData: ColorEditViewerData, name: ROPE, icon: Icons.IconFlavor] ~ { saInfo: SAFileInfo; aisViewerData _ NEW[ColorEditViewerDataRec]; SELECT TRUE FROM (saInfo _ Open3Windows[file, ["-red.ais", "-grn.ais", "-blu.ais"]])#nullSAInfo => NULL; (saInfo _ Open3Windows[file, ["-red.ais", "-green.ais", "-blue.ais"]])#nullSAInfo => NULL; (saInfo _ Open1Window[file, ".ais"])#nullSAInfo => NULL; (saInfo _ Open1Window[file, "-8bit.ais"])#nullSAInfo => NULL; (saInfo _ Open1Window[file, ""])#nullSAInfo => NULL; ENDCASE => { ERROR CantFindFile[]; }; aisViewerData.sa _ saInfo.sa; name _ saInfo.name; icon _ SampleArrayIcons.NewIcon[sa: aisViewerData.sa, layer: MIN[1, aisViewerData.sa.n-1]]; --Get green for rgb, otherwise just the black aisViewerData.sm _ SampleMapOps.Create[sSize: aisViewerData.sa.sSize, fSize: aisViewerData.sa.fSize, bitsPerSample: 1]; SampleMapOps.Clear[sampleMap: aisViewerData.sm]; }; Open1Window: PROC [file: ROPE, windowName: ROPE] RETURNS [saInfo: SAFileInfo] ~ { OpenWindowsWithoutCatchingErrorsButUnwinding: PROC ~ { saInfo.name _ FS.FileInfo[ name: Rope.Cat[file, windowName], wDir: CommandTool.CurrentWorkingDirectory[] ].fullFName; saInfo.sa _ SampleArrays.FromAIS[name: saInfo.name]; }; OpenWindowsWithoutCatchingErrorsButUnwinding[! ANY => GOTO ReturnNoWindows]; EXITS ReturnNoWindows => RETURN [nullSAInfo] }; Open3Windows: PROC [file: ROPE, windowNames: ARRAY Separation OF ROPE] RETURNS [saInfo: SAFileInfo] ~ { OpenWindowsWithoutCatchingErrorsButUnwinding: PROC ~ { files: ARRAY Separation OF ROPE; FOR s: Separation IN Separation DO --Check the existence of files, and remember names files[s] _ FS.FileInfo[ name: Rope.Cat[file, windowNames[s]], wDir: CommandTool.CurrentWorkingDirectory[] ].fullFName; ENDLOOP; saInfo.name _ Rope.Cat[file, "-*.ais"]; saInfo.sa _ SampleArrays.Join3AIS[name1: files[red], name2: files[green], name3: files[blue]]; }; OpenWindowsWithoutCatchingErrorsButUnwinding[! ANY => GOTO ReturnNoWindows]; EXITS ReturnNoWindows => RETURN [nullSAInfo] }; AdjustTransformation: PROC [data: ColorEditViewerData, m: Transformation] ~ { SetTransformation[data, ImagerTransformation.Concat[data.m, m]]; }; SetTransformation: PROC [data: ColorEditViewerData, m: Transformation] ~ { data.m _ m; data.mInverse _ ImagerTransformation.Invert[m: m]; data.mInverse.a _ 1.0/m.a; --Clean up errors from ImagerTransformation data.mInverse.e _ 1.0/m.e; data.picture _ SampledByRectangle[data.mInverse, [sMin: 0, fMin: 0, sMax: data.sa.sSize-1, fMax: data.sa.fSize-1], 0, 0]; data.fast _ SampleArrayDisplay.BuildSampler[min: MAX[data.picture.fMin, 0], size: 640, m: data.mInverse.a, b: data.mInverse.c]; data.slow _ SampleArrayDisplay.BuildSampler[min: MAX[data.picture.sMin, 0], size: 640, m: data.mInverse.e, b: data.mInverse.f]; }; AdjustRect: PROC [relative, absolute: DeviceRectangle] RETURNS [result: DeviceRectangle] ~ INLINE { result _ ImagerPixelMap.Intersect[absolute, [sMin: relative.sMin+absolute.sMin, fMin: relative.fMin+absolute.fMin, sSize: relative.sSize, fSize: relative.fSize]]; }; ColorEditViewersPaint: PRIVATE ViewerClasses.PaintProc ~ { aisViewerData: ColorEditViewerData ~ NARROW[self.data]; screen: SampleArrayDisplay.Screen; rect: ImagerPixelMap.DeviceRectangle; [screen, rect] _ SampleArrayDisplay.ViewerInformation[self]; SampleArrayDisplay.Display[screen: screen, rect: rect, sa: aisViewerData.sa, sm: aisViewerData.sm, slow: aisViewerData.slow, fast: aisViewerData.fast, picRect: AdjustRect[aisViewerData.picture, rect]]; }; FitTransformation: PROC [data: ColorEditViewerData] RETURNS [m: Transformation] ~ { scale: REAL _ MIN[640.0/data.sa.fSize, 480.0/data.sa.sSize]; scale _ IF scale>=1.0 THEN (REAL[RealOps.RoundLI[scale, [round: rm]]]) ELSE (1.0/REAL[RealOps.RoundLI[1.0/scale, [round: rp]]]); m _ ImagerTransformation.Cat[ m1: ImagerTransformation.Translate[t: [-data.sa.fSize/2.0, -data.sa.sSize/2.0]], m2: ImagerTransformation.Scale[s: scale], m3: ImagerTransformation.Translate[t: [640.0/2, 480.0/2]] ]; }; AISVScroll: ViewerClasses.ScrollProc = { data: ColorEditViewerData ~ NARROW[self.data]; min: REAL ~ ImagerTransformation.Transform[m: data.m, v: [0,0]].y; max: REAL ~ ImagerTransformation.Transform[m: data.m, v: [data.sa.fSize, data.sa.sSize]].y; size: REAL ~ (max-min)/100.0; SELECT op FROM query => { top _ MIN[100, MAX[0, Real.RoundI[-min/size]]]; bottom _ MIN[100, MAX[0, Real.RoundI[(self.ch-min)/size]]]; RETURN; }; thumb => { scale: REAL ~ data.m.a; tx: REAL ~ data.m.c; ty: REAL ~ -amount/100.0*data.sa.sSize; SetTransformation[data, ImagerTransformation.Create[a: scale, b: 0, c: tx, d: 0, e: scale, f: ty]]; }; up => AdjustTransformation[data, ImagerTransformation.Translate[t: [0, -amount]]]; down => AdjustTransformation[data, ImagerTransformation.Translate[t: [0, amount]]]; ENDCASE => ERROR; SELECT TRUE FROM shift AND control => SetTransformation[data, FitTransformation[data]]; control => AdjustTransformation[data, ImagerTransformation.Scale[s: 5]]; shift => AdjustTransformation[data, ImagerTransformation.Scale[s: 2]]; ENDCASE; ViewerOps.PaintViewer[viewer: self, hint: client]; }; AISHScroll: ViewerClasses.HScrollProc = { data: ColorEditViewerData ~ NARROW[self.data]; min: REAL ~ ImagerTransformation.Transform[m: data.m, v: [0,0]].x; max: REAL ~ ImagerTransformation.Transform[m: data.m, v: [data.sa.fSize, data.sa.sSize]].x; size: REAL ~ (max-min)/100.0; SELECT op FROM query => { left _ MIN[100, MAX[0, Real.RoundI[-min/size]]]; right _ MIN[100, MAX[0, Real.RoundI[(self.cw-min)/size]]]; RETURN; }; thumb => { scale: REAL ~ data.m.a; tx: REAL ~ -amount/100.0*data.sa.fSize; ty: REAL ~ data.m.f; SetTransformation[data, ImagerTransformation.Create[a: scale, b: 0, c: tx, d: 0, e: scale, f: ty]]; }; left => AdjustTransformation[data, ImagerTransformation.Translate[t: [-amount, 0]]]; right => AdjustTransformation[data, ImagerTransformation.Translate[t: [amount, 0]]]; ENDCASE => ERROR; SELECT TRUE FROM shift AND control => SetTransformation[data, ImagerTransformation.Scale[s: 1]]; control => AdjustTransformation[data, ImagerTransformation.Scale[s: 5]]; shift => AdjustTransformation[data, ImagerTransformation.Scale[s: 2]]; ENDCASE; ViewerOps.PaintViewer[viewer: self, hint: client]; }; cursorHandles: ARRAY Cursors.CursorType OF Cursors.CursorHandle _ ALL[NIL]; cursorSampleMaps: ARRAY Cursors.CursorType OF SampleMap _ ALL[NIL]; CursorArray: TYPE ~ ARRAY [0..16) OF PACKED ARRAY [0..16) OF [0..1]; EnsureSampleMapForCursor: PROC [cursor: Cursors.CursorType] ~ { IF cursorSampleMaps[cursor]=NIL THEN ERROR; }; CreateCursor: PROC [hotX, hotY: INTEGER, bits: CursorArray] RETURNS [t: Cursors.CursorType] ~ { h: Cursors.CursorHandle _ NEW[Cursors.CursorRec]; h.bits _ LOOPHOLE[bits]; h.info _ [ type: Cursors.NewCursor[h.bits, hotX, hotY], hotX: hotX, hotY: hotY ]; t _ h.info.type; cursorHandles[t] _ h; cursorSampleMaps[t] _ SampleMapOps.Create[sSize: 16, fSize: 16, bitsPerSample: 1]; SampleMapOps.Clear[cursorSampleMaps[t]]; FOR s: [0..16) IN [0..16) DO FOR f: [0..16) IN [0..16) DO IF bits[s][f]=1 THEN SampleMapOps.PutSample[cursorSampleMaps[t], [s, f], 1]; ENDLOOP; ENDLOOP; }; contourCursor: Cursors.CursorType ~ CreateCursor[hotX: -7, hotY: -7, bits: [ [0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0], [0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0], [1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0], [0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0], [0,0,1,0,0,0,0,1,1,0,0,0,0,1,0,0], [0,0,1,0,0,0,0,1,1,0,0,0,0,1,0,0], [0,0,1,0,0,0,1,0,0,1,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], [1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1], [0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0], [0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0] ]]; selBrushCursor: Cursors.CursorType ~ CreateCursor[hotX: 0, hotY: 0, bits: [ [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ]]; matchColorCursor: Cursors.CursorType ~ CreateCursor[hotX: -7, hotY: -7, bits: [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0], [0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0], [0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0], [0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0], [0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0], [0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0], [0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0], [0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0], [0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0], [0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ]]; edgeBrushCursor: Cursors.CursorType ~ CreateCursor[hotX: -7, hotY: -7, bits: [ [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0], [0,0,0,1,1,0,1,0,1,0,1,1,0,0,0,0], [0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0], [0,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0], [0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,0], [1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0], [1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0], [1,0,1,0,0,0,0,1,0,0,0,0,1,0,1,0], [1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0], [1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0], [0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,0], [0,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0], [0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0], [0,0,0,1,1,0,1,0,1,0,1,1,0,0,0,0], [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ]]; selColorBrushCursor: Cursors.CursorType ~ CreateCursor[hotX: -7, hotY: -7, bits: [ [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0], [0,0,0,1,1,0,1,0,1,0,1,1,0,0,0,0], [0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0], [0,1,1,0,1,0,1,0,1,0,1,0,1,1,0,0], [0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0], [1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0], [1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,0], [1,0,1,0,0,0,0,1,0,0,0,0,1,0,1,0], [1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,0], [1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0], [0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0], [0,1,1,0,1,0,1,0,1,0,1,0,1,1,0,0], [0,0,1,1,0,1,0,1,0,1,0,1,1,0,0,0], [0,0,0,1,1,0,1,0,1,0,1,1,0,0,0,0], [0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ]]; rectCursor: Cursors.CursorType ~ CreateCursor[hotX: -7, hotY: -7, bits: [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0], [0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ]]; selPixelCursor: Cursors.CursorType ~ CreateCursor[hotX: -7, hotY: -7, bits: [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,1,1,1,1,0,0,1,0,0,1,1,1,1,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ]]; Notify: ENTRY ViewerClasses.NotifyProc = { ENABLE UNWIND => NULL; data: ColorEditViewerData ~ NARROW[self.data]; ts: TIPState ~ data.ts; FOR each: LIST OF REF ANY _ input, each.rest UNTIL each=NIL DO IF ts.state=menu THEN { data.ts.state _ ready; --Kluge!!! }; WITH each.first SELECT FROM atom: ATOM => { SELECT atom FROM $Drag => Drag[self, data]; $Press => Press[self, data]; $Abort => Abort [self, data]; $Release => Release[self, data]; $CheckAbort => CheckAbort[self, data]; $Menu => TRUSTED {Process.Detach[FORK CallUpMenu[NIL, self, data]]}; $Shift => data.ts.shift _ TRUE; $Ctrl => data.ts.ctrl _ TRUE; $Double => data.ts.double _ TRUE; $Ignore => NULL; ENDCASE => MessageWindow.Append[Rope.Concat["Unknown atom given to AISView: ", Atom.GetPName[atom]], TRUE]; }; coords: TIPUser.TIPScreenCoords => { data.ts.pos _ [ mouseX: MAX[data.picture.fMin, MIN[data.picture.fMin+data.picture.fSize-1, coords.mouseX]], mouseY: MAX[data.picture.sMin, MIN[data.picture.sMin+data.picture.sSize-1, self.ch - coords.mouseY]], color: coords.color ]; }; z: TIPTables.TIPTime => NULL; ENDCASE => MessageWindow.Append["Unknown input given to ColorEditViewer.", TRUE]; ENDLOOP; }; Drag: TIPCmdProc ~ { IF data.ts.state#mouseDown THEN RETURN; --I.e. something has gone wrong IF op[data.ts.button].drag#NIL THEN op[data.ts.button].drag[self, data ! Quit => GOTO Abort]; EXITS Abort => Abort[self, data]; }; Press: TIPCmdProc ~ { IF data.ts.state#ready THEN RETURN; --Why shouldn't it be ready? InputFocus.SetInputFocus[self: self]; IF op[data.ts.button].press#NIL THEN op[data.ts.button].press[self, data ! Quit => GOTO Abort]; data.ts.state _ mouseDown; EXITS Abort => Abort[self, data]; }; Abort: TIPCmdProc ~ { IF data.ts.state=abort THEN RETURN; --Only Abort once IF op[data.ts.button].abort#NIL THEN op[data.ts.button].abort[self, data ! Quit => CONTINUE]; data.ts.state _ abort; }; Release: TIPCmdProc ~ { IF data.ts.state=mouseDown THEN { IF op[data.ts.button].release#NIL THEN op[data.ts.button].release[self, data ! Quit => CONTINUE]; IF data.ts.button=left THEN data.ts.toUndo _ op[data.ts.button]; --Remember for Undo }; data.ts.state _ ready; data.ts.double _ data.ts.shift _ data.ts.ctrl _ FALSE; data.ts.button _ left; }; CheckAbort: TIPCmdProc ~ { IF data.ts.state#ready THEN { Abort[self, data]; --Abort previous action data.ts.state _ ready; --And become ready }; }; UndoProc: REF TIPCmdProc _ NEW[TIPCmdProc _ Undo]; Undo: TIPCmdProc ~ { IF data.ts.state#ready THEN ERROR; IF data.ts.toUndo=NIL OR data.ts.toUndo.undo=NIL THEN { [] _ PopUpMenu.RequestSelection[NIL, LIST["Can't Undo."], 1]; } ELSE { data.ts.toUndo.undo[self, data ! Quit => CONTINUE]; }; }; destination: SelectRegions.Operation _ on; InvertIfDouble: PROC [d: SelectRegions.Operation, double: BOOL] RETURNS [SelectRegions.Operation] ~ INLINE { RETURN [IF double THEN SELECT d FROM on => off, off => on, flip => flip ENDCASE => ERROR ELSE d] }; rectTitle: ROPE ~ "by Rectangle"; RectPress: TIPCmdProc ~ { tip: TIPUser.TIPScreenCoordsRec ~ data.ts.from _ data.ts.pos; data.ts.rect _ SamplesRectangle[data.mInverse, tip.mouseX, tip.mouseY, tip.mouseX, tip.mouseY]; }; RectDrag: TIPCmdProc ~ { tip: TIPUser.TIPScreenCoordsRec ~ data.ts.pos; rect: Rectangle ~ SamplesRectangle[data.mInverse, data.ts.from.mouseX, data.ts.from.mouseY, tip.mouseX, tip.mouseY]; DoDifferences: PROC [r: Rectangle] ~ { SelectRegions.FillRectangle[sm: data.sm, rect: r, op: flip]; PaintRectangle[self, data, r]; }; SelectRegions.Differences[a: data.ts.rect, b: rect, action: DoDifferences]; data.ts.rect _ rect; }; RectRelease: TIPCmdProc ~ TRUSTED { op: SelectRegions.Operation ~ InvertIfDouble[destination, data.ts.double]; SELECT op FROM on, flip => data.extent _ ExtendExtent[data.extent, data.ts.rect]; off => data.extent _ DeextendExtent[data.extent, data.ts.rect]; ENDCASE => ERROR; IF op#flip THEN { --If op=flip, then we're already done SelectRegions.FillRectangle[sm: data.sm, rect: data.ts.rect, op: op]; PaintRectangle[self, data, data.ts.rect]; }; }; RectAbort: TIPCmdProc ~ TRUSTED { SelectRegions.FillRectangle[sm: data.sm, rect: data.ts.rect, op: flip]; PaintRectangle[self, data, data.ts.rect]; }; selectRectOperation: Operation _ InstallOperation[op: [ press: RectPress, drag: RectDrag, release: RectRelease, abort: RectAbort, cursor: rectCursor ]]; contourTitle: ROPE ~ "by Contour"; ContourPress: TIPCmdProc ~ { Action: PROC ~ { extent: Rectangle; loc: CVEC; [x: loc.f, y: loc.s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; extent _ SelectRegions.Fill[sm: data.sm, loc: loc]; PaintRectangle[self, data, extent]; data.extent _ ExtendExtent[data.extent, extent]; }; WaitFor[Action, self]; }; selectContourOperation: Operation _ InstallOperation[op: [ press: ContourPress, cursor: contourCursor ]]; MatchColorPress: TIPCmdProc ~ { Action: PROC ~ { extent: Rectangle; loc: CVEC; [x: loc.f, y: loc.s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; extent _ SelectRegions.MatchColorFill[sm: data.sm, sa: data.sa, loc: loc, colorDistance: colorBrushColorDistance]; PaintRectangle[self, data, extent]; data.extent _ ExtendExtent[data.extent, extent]; }; WaitFor[Action, self]; }; selectMatchColorOperation: Operation _ InstallOperation[op: [ press: MatchColorPress, cursor: matchColorCursor ]]; EdgeBrushPress: TIPCmdProc ~ { GetColor: PROC RETURNS [rgb: SelectRegions.RGB] ~ { RETURN [[ r: SampleArrays.GetSample[data.sa, 0, [s: s, f: f]], g: SampleArrays.GetSample[data.sa, 1, [s: s, f: f]], b: SampleArrays.GetSample[data.sa, 2, [s: s, f: f]] ]]; }; s, f: CARDINAL; [x: f, y: s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; SELECT TRUE FROM data.ts.double => ContourPress[self, data]; data.ts.shift OR data.ts.ctrl => data.referentColor _ GetColor[]; ENDCASE => { --Normal case data.ts.rect _ [sMin: Lo[s], fMin: Lo[f], sMax: s+brushSize, fMax: f+brushSize]; SelectRegions.DiscriminateColor[ sm: data.sm, sa: data.sa, near: GetColor[], far: data.referentColor, bounds: data.ts.rect ]; PaintRectangle[self, data, data.ts.rect]; data.extent _ ExtendExtent[data.extent, data.ts.rect]; }; }; EdgeBrushDrag: TIPCmdProc ~ { SELECT TRUE FROM data.ts.double OR data.ts.shift OR data.ts.ctrl => NULL; --Ignore because a double is a fill, and shift or control select the color to ignore ENDCASE => { Action: PROC [r: Rectangle] ~ { SelectRegions.DiscriminateColor[sm: data.sm, sa: data.sa, near: [r: red, g: green, b: blue], far: data.referentColor, bounds: r]; PaintRectangle[self, data, r]; data.extent _ ExtendExtent[data.extent, r]; }; red, green, blue: CARDINAL; s, f: CARDINAL; rectangle: Rectangle; [x: f, y: s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; red _ SampleArrays.GetSample[data.sa, 0, [s: s, f: f]]; green _ SampleArrays.GetSample[data.sa, 1, [s: s, f: f]]; blue _ SampleArrays.GetSample[data.sa, 2, [s: s, f: f]]; rectangle _ [sMin: Lo[s], fMin: Lo[f], sMax: s+brushSize, fMax: f+brushSize]; SelectRegions.Differences[a: rectangle, b: SelectRegions.Intersect[rectangle, data.ts.rect], action: Action]; data.ts.rect _ rectangle; }; }; edgeBrushOperation: Operation _ InstallOperation[op: [ press: EdgeBrushPress, drag: EdgeBrushDrag, cursor: edgeBrushCursor ]]; brushSize: PUBLIC CARDINAL _ 16; colorBrushColorDistance: PUBLIC CARDINAL _ 16; Lo: PROC [val: CARDINAL] RETURNS [CARDINAL] ~ INLINE { RETURN [MAX[val, brushSize]-brushSize] }; SelColorBrushPress: TIPCmdProc ~ { SELECT TRUE FROM data.ts.double => ContourPress[self, data]; ENDCASE => { --Normal case s, f: CARDINAL; [x: f, y: s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; data.ts.rect _ [sMin: Lo[s], fMin: Lo[f], sMax: s+brushSize, fMax: f+brushSize]; SelectRegions.MatchColor[ sm: data.sm, sa: data.sa, rgb: [ r: SampleArrays.GetSample[data.sa, 0, [s: s, f: f]], g: SampleArrays.GetSample[data.sa, 1, [s: s, f: f]], b: SampleArrays.GetSample[data.sa, 2, [s: s, f: f]] ], bounds: data.ts.rect, colorDistance: colorBrushColorDistance ]; PaintRectangle[self, data, data.ts.rect]; data.extent _ ExtendExtent[data.extent, data.ts.rect]; }; }; SelColorBrushDrag: TIPCmdProc ~ { SELECT TRUE FROM data.ts.double => NULL; --Ignore because a double is a fill ENDCASE => { Action: PROC [r: Rectangle] ~ { SelectRegions.MatchColor[sm: data.sm, sa: data.sa, rgb: [r: red, g: green, b: blue], bounds: r, colorDistance: colorBrushColorDistance]; PaintRectangle[self, data, r]; data.extent _ ExtendExtent[data.extent, r]; }; red, green, blue: CARDINAL; s, f: CARDINAL; rectangle: Rectangle; [x: f, y: s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; red _ SampleArrays.GetSample[data.sa, 0, [s: s, f: f]]; green _ SampleArrays.GetSample[data.sa, 1, [s: s, f: f]]; blue _ SampleArrays.GetSample[data.sa, 2, [s: s, f: f]]; rectangle _ [sMin: Lo[s], fMin: Lo[f], sMax: s+brushSize, fMax: f+brushSize]; SelectRegions.Differences[a: rectangle, b: SelectRegions.Intersect[rectangle, data.ts.rect], action: Action]; data.ts.rect _ rectangle; }; }; selectColorBrushOperation: Operation _ InstallOperation[op: [ press: SelColorBrushPress, drag: SelColorBrushDrag, cursor: selColorBrushCursor ]]; SelBrushPress, SelBrushDrag: TIPCmdProc ~ TRUSTED { op: SelectRegions.Operation ~ InvertIfDouble[destination, data.ts.double]; current: Cursors.CursorType ~ selectBrushOperation.cursor; info: Cursors.CursorInfo ~ cursorHandles[current].info; loc: CVEC; extent: Rectangle; [x: loc.f, y: loc.s] _ Samples[data.mInverse, data.ts.pos.mouseX-info.hotX, data.ts.pos.mouseY-info.hotY]; EnsureSampleMapForCursor[current]; SelectRegions.PaintBrush[sm: data.sm, loc: loc, op: op, brush: cursorSampleMaps[current]]; extent _ [sMin: loc.s, fMin: loc.f, sMax: loc.s+16, fMax: loc.f+16]; PaintRectangle[self, data, extent]; IF op#off THEN data.extent _ ExtendExtent[data.extent, extent]; }; selectBrushOperation: Operation _ InstallOperation[op: [ press: SelBrushPress, drag: SelBrushDrag, cursor: selBrushCursor ]]; SelPixelPress: TIPCmdProc ~ TRUSTED { op: SelectRegions.Operation ~ InvertIfDouble[destination, data.ts.double]; loc: CVEC; [x: loc.f, y: loc.s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; SelectRegions.PaintPixel[sm: data.sm, loc: loc, op: op]; data.ts.loc _ loc; PaintRectangle[self, data, [sMin: loc.s, fMin: loc.f, sMax: loc.s+1, fMax: loc.f+1]]; IF op#off THEN data.extent _ ExtendExtent[data.extent, [sMin: loc.s, fMin: loc.f, sMax: loc.s+1, fMax: loc.f+1]]; }; SelPixelDrag: TIPCmdProc ~ { PaintPoint: PROC [p: PixelGraphics.Point] ~ { loc: CVEC ~ [s: p.y, f: p.x]; SelectRegions.PaintPixel[sm: data.sm, loc: loc, op: op]; }; op: SelectRegions.Operation ~ InvertIfDouble[destination, data.ts.double]; to: CVEC; extent: Rectangle; fFrom: CARDINAL ~ data.ts.loc.f; sFrom: CARDINAL ~ data.ts.loc.s; [x: to.f, y: to.s] _ Samples[data.mInverse, data.ts.pos.mouseX, data.ts.pos.mouseY]; PixelGraphics.InterpolateLines[LIST[[fFrom, sFrom], [to.f, to.s]], PaintPoint]; --Interpolate intermediate position extent _ [sMin: MIN[sFrom, to.s], fMin: MIN[fFrom, to.f], sMax: MAX[sFrom, to.s]+1, fMax: MAX[fFrom, to.f]+1]; PaintRectangle[self, data, extent]; IF op#off THEN data.extent _ ExtendExtent[data.extent, extent]; data.ts.loc _ to; }; selectPixelOperation: Operation _ InstallOperation[op: [ press: SelPixelPress, drag: SelPixelDrag, cursor: selPixelCursor ]]; InstallOperation: PROC [op: OperationRec, fastChar: CHAR _ ' ] RETURNS [ref: Operation] ~ { ref _ NEW[OperationRec _ op]; }; InstallOperations: PROC ~ { op[left] _ selectMatchColorOperation; op[right] _ selectPixelOperation; }; ts: TajoMenus.TajoSystem; CallUpMenu: PROC [title: ROPE, self: ColorEditViewer, data: ColorEditViewerData] ~ { ref: REF _ TajoMenus.Select[ts, title]; IF ref=NIL THEN RETURN; WITH ref SELECT FROM operation: Operation => { --I.e. something to install op[data.ts.button] _ operation; --Install the operation self.class.cursor _ operation.cursor; --Set the cursor type }; proc: REF TIPCmdProc => proc[self, data]; ENDCASE => ERROR; data.ts.state _ menu; TRUSTED {data.ts.ignoreUntil _ Intime.ReadEventTime[];} }; InstallMenus: PROC ~ { ts _ TajoMenus.CreateTajoSystem[LIST[ [title: "SELECT", options: LIST[ [rope: "by Rectangle", action: selectRectOperation], [rope: "by Hue Match", action: selectMatchColorOperation], [rope: "by Color Brush", action: selectColorBrushOperation], [rope: "by Edge Brush", action: edgeBrushOperation], [rope: "by Filling", action: selectContourOperation], [rope: "by Brush", action: selectBrushOperation], [rope: "by Pixel", action: selectPixelOperation], [rope: "Select all", action: SelectAllProc], [rope: "Clear all", action: ClearAllProc] ]], [title: "EDIT", options: LIST[ [rope: "Apply", action: ApplyProc], [rope: "Save", action: SaveProc], [rope: "Save Mask", action: SaveMaskProc], [rope: "Load Mask", action: LoadMaskProc], [rope: "Save Log", action: SaveLogProc], [rope: "Recover Log", action: LoadLogProc] ]], [title: "DESTINATION", options: LIST[ [rope: "to Foreground", action: ToForegroundProc], [rope: "to Background", action: ToBackgroundProc], [rope: "to Opposite", action: ToOtherProc] ]], [title: "OPTIONS", options: LIST[ [rope: "Undo", action: UndoProc], [rope: "White Cursor", action: MakeCursorWhiteProc], [rope: "Black Cursor", action: MakeCursorBlackProc] ]] ]]; }; ClearAllProc: REF TIPCmdProc _ NEW[TIPCmdProc _ ClearAll]; ClearAll: TIPCmdProc ~ { rect: Rectangle ~ [0, 0, data.sa.sSize, data.sa.fSize]; SelectRegions.FillRectangle[sm: data.sm, rect: rect, op: InvertIfDouble[destination, TRUE]]; --i.e. invert the destination PaintRectangle[self, data, rect]; data.extent _ SelectRegions.nullRectangle; }; SelectAllProc: REF TIPCmdProc _ NEW[TIPCmdProc _ SelectAll]; SelectAll: TIPCmdProc ~ { rect: Rectangle ~ [0, 0, data.sa.sSize, data.sa.fSize]; SelectRegions.FillRectangle[sm: data.sm, rect: SelectRegions.nullRectangle, op: destination]; PaintRectangle[self, data, rect]; data.extent _ [sMin: 0, fMin: 0, sMax: data.sm.sSize-1, fMax: data.sm.fSize-1]; }; SaveProc: REF TIPCmdProc _ NEW[TIPCmdProc _ Save]; Save: TIPCmdProc ~ { Action: PROC ~ { SampleArrays.Save[data.sa]; data.changeLog _ NIL; --Erase the change log }; WaitFor[Action, self]; }; SaveLogProc: REF TIPCmdProc _ NEW[TIPCmdProc _ SaveLog]; SaveLog: TIPCmdProc ~ { Action: PROC ~ { fileName: ROPE ~ Rope.Cat[SampleArrays.FileNameRoot[data.sa], ".changeLog"]; fileStream: IO.STREAM ~ FS.StreamOpen[fileName, create]; IO.PutRope[self: fileStream, r: data.changeLog]; IO.Close[self: fileStream]; }; WaitFor[Action, self]; }; LoadLogProc: REF TIPCmdProc _ NEW[TIPCmdProc _ LoadLog]; LoadLog: TIPCmdProc ~ { Action: PROC ~ { IndicateFailure: PROC ~ { [] _ PopUpMenu.RequestSelection[NIL, LIST[Rope.Cat["Can't find file ", fileName, " or it's not an extent file of appropriate dimensions."]], 1]; }; fileName: ROPE ~ Rope.Cat[SampleArrays.FileNameRoot[data.sa], ".changeLog"]; fileStream: IO.STREAM ~ FS.StreamOpen[fileName]; ropeStream: IO.STREAM ~ IO.ROS[]; DO IO.PutChar[self: ropeStream, char: IO.GetChar[self: fileStream ! IO.EndOfStream => EXIT]]; ENDLOOP; data.changeLog _ IO.RopeFromROS[self: ropeStream]; ColorEditOps.ApplyLog[data.changeLog, data.sa]; ViewerOps.PaintViewer[viewer: self, hint: client, clearClient: FALSE]; }; WaitFor[Action, self]; }; SaveMaskProc: REF TIPCmdProc _ NEW[TIPCmdProc _ SaveMask]; SaveMask: TIPCmdProc ~ { Action: PROC ~ { fileName: ROPE ~ Rope.Cat[SampleArrays.FileNameRoot[data.sa], "-mask.extent"]; data.extent _ ClipExtent[extent: data.extent, sSize: data.sa.sSize, fSize: data.sa.fSize]; ColorEditOps.SaveExtentOfMask[data.sm, data.extent, fileName]; }; WaitFor[Action, self]; }; LoadMaskProc: REF TIPCmdProc _ NEW[TIPCmdProc _ LoadMask]; LoadMask: TIPCmdProc ~ { Action: PROC ~ { IndicateFailure: PROC ~ { [] _ PopUpMenu.RequestSelection[NIL, LIST[Rope.Cat["Can't find file ", fileName, " or it's not an extent file of appropriate dimensions."]], 1]; }; fileName: ROPE ~ Rope.Cat[SampleArrays.FileNameRoot[data.sa], "-mask.extent"]; data.extent _ ColorEditOps.ReadExtentOfMask[data.sm, fileName ! FS.Error => {IndicateFailure[]; CONTINUE}]; PaintRectangle[self, data, [sMin: 0, sMax: data.sm.sSize-1, fMin: 0, fMax: data.sm.fSize-1]]; }; WaitFor[Action, self]; }; ApplyProc: REF TIPCmdProc _ NEW[TIPCmdProc _ Apply]; Apply: TIPCmdProc ~ { Action: PROC ~ { trcs: SelectRegions.TRCSequence ~ NEW[SelectRegions.TRCSequenceRep[data.sa.n]]; trc: REF TRC ~ AdjustColorStd.CurrentTRC[]; FOR i: NAT IN [0..trcs.n) DO trcs[i] _ trc; ENDLOOP; data.extent _ ClipExtent[extent: data.extent, sSize: data.sa.sSize, fSize: data.sa.fSize]; SelectRegions.ApplyTRC[data.sm, data.sa, trcs, data.extent]; data.changeLog _ data.changeLog.Cat[ColorEditOps.LogApply[trc, data.sm, data.extent]]; data.extent _ SelectRegions.nullRectangle; AdjustColorStd.NormalizeAdjustments[]; SampleMapOps.Clear[sampleMap: data.sm]; ViewerOps.PaintViewer[viewer: self, hint: client, clearClient: FALSE]; }; WaitFor[Action, self]; }; MakeCursorWhiteProc: REF TIPCmdProc _ NEW[TIPCmdProc _ MakeCursorWhite]; MakeCursorWhite: TIPCmdProc ~ { [] _ Terminal.SetColorCursorPresentation[term, onesAreWhite]; }; MakeCursorBlackProc: REF TIPCmdProc _ NEW[TIPCmdProc _ MakeCursorBlack]; MakeCursorBlack: TIPCmdProc ~ { [] _ Terminal.SetColorCursorPresentation[term, onesAreBlack]; }; ToForegroundProc: REF TIPCmdProc _ NEW[TIPCmdProc _ ToForeground]; ToForeground: TIPCmdProc ~ { destination _ on; }; ToBackgroundProc: REF TIPCmdProc _ NEW[TIPCmdProc _ ToBackground]; ToBackground: TIPCmdProc ~ { destination _ off; }; ToOtherProc: REF TIPCmdProc _ NEW[TIPCmdProc _ ToOther]; ToOther: TIPCmdProc ~ { destination _ flip; }; ExtendExtent: PROC [extent, plus: Rectangle] RETURNS [result: Rectangle] ~ { IF extent=SelectRegions.nullRectangle THEN RETURN [plus]; RETURN [[ sMin: MIN[extent.sMin, plus.sMin], fMin: MIN[extent.fMin, plus.fMin], sMax: MAX[extent.sMax, plus.sMax], fMax: MAX[extent.fMax, plus.fMax] ]] }; DeextendExtent: PROC [extent, minus: Rectangle] RETURNS [result: Rectangle] ~ { result _ extent; IF result=SelectRegions.nullRectangle THEN RETURN; SELECT TRUE FROM minus.fMin>extent.fMin => IF minus.sMax~extent.sMin THEN result.fMax _ minus.fMin; minus.sMin>extent.sMin => IF minus.sMax~ IF minus.sMax~ result.sMin _ minus.sMax; ENDCASE => result _ SelectRegions.nullRectangle; }; ClipExtent: PROC [extent: Rectangle, sSize, fSize: CARDINAL] RETURNS [clipped: Rectangle] ~ { clipped _ extent; clipped.sMax _ MIN[clipped.sMax, sSize-1]; clipped.fMax _ MIN[clipped.fMax, fSize-1]; }; WaitFor: PROC [action: PROC, viewer: Viewer, cursorInViewerNow: BOOL _ TRUE] ~ { cursor: Cursors.CursorType ~ viewer.class.cursor; viewer.class.cursor _ hourGlass; IF cursorInViewerNow THEN Cursors.SetCursor[hourGlass]; action[]; viewer.class.cursor _ cursor; IF cursorInViewerNow THEN Cursors.SetCursor[cursor]; }; Samples: PROC [m: Transformation, cx, cy: INTEGER] RETURNS [x, y: CARDINAL] ~ { RETURN [x: Real.RoundC[m.a*cx + m.c], y: Real.RoundC[m.e*cy + m.f]]; }; SamplesRectangle: PROC [m: Transformation, cx1, cy1, cx2, cy2: INTEGER] RETURNS [r: Rectangle] ~ { temp: CARDINAL; [r.fMin, r.sMin] _ Samples[m, cx1, cy1]; [r.fMax, r.sMax] _ Samples[m, cx2, cy2]; IF r.sMin>r.sMax THEN {temp _ r.sMin; r.sMin _ r.sMax; r.sMax _ temp}; IF r.fMin>r.fMax THEN {temp _ r.fMin; r.fMin _ r.fMax; r.fMax _ temp}; }; SampledBy: PROC [m: Transformation, x, y: CARDINAL] RETURNS [cxlo, cylo, cxhi, cyhi: INTEGER] ~ { cxlo _ RealOps.RoundI[a: (x-0.5-m.c)/m.a, m: [round: rp]]; cxhi _ RealOps.RoundI[a: (x+0.5-m.c)/m.a, m: [round: rm]]; cylo _ RealOps.RoundI[a: (y-0.5-m.f)/m.e, m: [round: rp]]; cyhi _ RealOps.RoundI[a: (y+0.5-m.f)/m.e, m: [round: rm]]; }; SampledByRectangle: PROC [m: Transformation, rect: Rectangle, offsetX, offsetY: CARDINAL] RETURNS [device: DeviceRectangle] ~ { cxlo1, cylo1, cxhi1, cyhi1, cxlo2, cylo2, cxhi2, cyhi2: INTEGER; minx, miny: INTEGER; [cxlo1, cylo1, cxhi1, cyhi1] _ SampledBy[m, rect.fMin, rect.sMin]; [cxlo2, cylo2, cxhi2, cyhi2] _ SampledBy[m, rect.fMax, rect.sMax]; minx _ MIN[cxlo1, cxlo2]; miny _ MIN[cylo1, cylo2]; device _ [fMin: minx+offsetX, fSize: MAX[cxhi1, cxhi2]-minx, sMin: miny+offsetY, sSize: MAX[cyhi1, cyhi2]-miny]; }; PaintRectangle: PROC [self: ColorEditViewer, data: ColorEditViewerData, r: Rectangle] ~ { screen: SampleArrayDisplay.Screen; bounds, region: DeviceRectangle; [screen, bounds] _ SampleArrayDisplay.ViewerInformation[self]; region _ SampledByRectangle[data.mInverse, r, bounds.fMin, bounds.sMin]; region _ [sMin: region.sMin-1, fMin: region.fMin-1, sSize: region.sSize+2, fSize: region.fSize+2]; --Be sloppy, but make sure we get it! SampleArrayDisplay.Display[screen: screen, rect: bounds, sa: NIL, sm: data.sm, slow: data.slow, fast: data.fast, picRect: AdjustRect[data.picture, bounds], onlyPaintIn: region]; }; Show: Commander.CommandProc ~ { files: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; column: ViewerClasses.Column _ IF ColorDisplay.GetColorDisplayStatus[].on THEN color ELSE left; FOR each: LIST OF ROPE _ files, each.rest UNTIL each=NIL DO [] _ Create[[name: each.first, iconic: TRUE, column: column]]; ENDLOOP; }; blink: ATOM _ NIL; Init: PROC ~ { aisViewerClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: ColorEditViewersPaint, tipTable: TIPUser.InstantiateNewTIPTable["ColorEdit.tip"], notify: Notify, scroll: AISVScroll, hscroll: AISHScroll, topDownCoordSys: TRUE, cursor: matchColorCursor ]]; ViewerOps.RegisterViewerClass[$ColorEditViewer, aisViewerClass]; InstallOperations[]; --Sets up left mouse-button operations InstallMenus[]; --Sets up default menu system Commander.Register[key: "ColorEdit", proc: Show, doc: "ColorEdit fileName  open an AIS viewer"]; }; Init[]; END. œColorEditViewersImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Eric Nickell, January 7, 1986 8:25:08 pm PST Viewer Creation and AIS File attachment corner1: VEC ~ ImagerTransformation.Transform[m: m, v: [0, 0]]; corner2: VEC ~ ImagerTransformation.Transform[m: m, v: [data.sa.fSize, data.sa.sSize]]; data.picture _ [ sMin: Real.RoundI[MIN[corner1.y, corner2.y]], fMin: Real.RoundI[MIN[corner1.x, corner2.x]], sSize: Real.RoundI[MAX[corner1.y, corner2.y]-MIN[corner1.y, corner2.y]], fSize: Real.RoundI[MAX[corner1.x, corner2.x]-MIN[corner1.x, corner2.x]] ]; [self: ViewerClasses.Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE] [self: ViewerClasses.Viewer, op: ViewerClasses.ScrollOp, amount: INTEGER, shift: BOOL _ FALSE, control: BOOL _ FALSE] RETURNS [top: INTEGER, bottom: INTEGER] [self: ViewerClasses.Viewer, op: ViewerClasses.HScrollOp, amount: INTEGER, shift: BOOL _ FALSE, control: BOOL _ FALSE] RETURNS [left: INTEGER, right: INTEGER] Cursors TIP Notify and Commands The following procedures, when coupled with an appropriate TIP table, implement the user interface for the AIS Viewer as: follows: Left Mouse Button: Controls the ``current'' operation, which is selectable from a wide variety. Each operation contains procedure to be called when the button is pressed, when it is dragged, when the operation is aborted, and when the button is released (see fuller details in Operations section). When the mouse button is pressed the state of the shift and control keys is remembered, and whether or not the button was double-clicked. Middle Mouse Button: Middle-clicking will bring up the AIS Viewer pop-up menus. (Double-clicking brings up the top-level menu, while single clicking tries to be smart about which menu the user is likely to want.) The menu system should return either a procedure to call, or an operation to install on the left mouse button. Right Mouse Button: Currently implemented like the left button, except that no provision has been made to change the operation found there. [self: ViewerClasses.Viewer, input: LIST OF REF ANY] Here, to recover from a PopUp menu Prespare for next Press For some reason, we can't Undo that last action Operations Select Rectangle Operation Select Contour Operation undo: ContourUndo, Select by Color Operation MatchColorUndo: TIPCmdProc ~ TRUSTED { SelectRegions.UndoFillOrMatchColor[]; }; undo: MatchColorUndo, Select by Edge Brush Operation Select by Color Brush Operation Select by Brush Operation Select by Pixel Operation Install the various operations title is the ROPE found in the menu system which will activate the operation on the left button Called at start-up Menus and such In this section, we install all the menus, and those menu items which immediately activate procedures (as opposed to setting up the operation hung on the left mouse button). Comment [title: "Title", options: LIST[ [rope: "Menu entry", action: REF], [rope: "Menu entry", action: REF], [rope: "Menu entry", action: REF], ]], SELECT EDIT DESTINATION OPTIONS Extents Other procedures Given user tip coords, tells which location in the pixel map will be sampled. x = Round[m.a*cxlo + m.c], and x = Round[m.a*(cxhi) + m.c], with cxlo being as small, and cxhi as large, as possible. Same, obviously, for y. Round[x] = RealOps.RoundC[x+0.5, [round: rm]], to be precise. Which is the same as: x IN [m.a*cx + m.c - 0.5, m.a*cx + m.c + 0.5) Which is the same as: x > m.a*cx + m.c - 0.5 x < m.a*cx + m.c + 0.5 Which is the same as (assuming m.a>0): cx < (x + 0.5 - m.c)/m.a cx > (x - 0.5 - m.c)/m.a Creates an ColorEditViewer for each file listed Register the new viewer class Κ3ΐ˜šœ™Icodešœ Οmœ=™HJ™,—J™šΟk ˜ J˜J˜K˜Kšœžœ$žœ˜=Jšœžœ ˜Jšœ žœ˜+Kšœ žœ:˜LKšœ žœ˜(Jšœ žœ(˜9KšœžœI˜VJšžœžœ˜'Kšœžœ˜Kšœžœ˜2KšœžœL˜fKšœ žœ ˜0Kšœžœ˜(Kšžœžœ1žœžœ˜SJšœžœ ˜Kšœžœ˜.Kšœ žœ˜#Kšœžœ ˜Kšœžœ˜Kšœžœ˜ Jšœžœžœ˜Kšœžœ4˜LKšœžœ ˜!Kšœ žœA˜SKšœ žœžœ#˜JKšœžœ₯žœ˜ΪKšœ žœ(˜7Kšœ žœ0˜>Kšœ žœ ˜Jšœžœ?˜LKšœžœžœ˜Kšœžœj˜}Kšœ ˜ —J˜K˜Jšœžœž˜#KšžœTžœ<žœΑ˜άJšžœ˜Jšœž˜šžœ+˜/J˜Jšžœžœžœ˜Jšžœžœžœ˜Jšžœžœ žœ˜Jšœžœ˜$J˜Kšœ žœΟc˜:Jšœžœžœ˜7Jšœžœžœ2˜UJ˜J˜Jšœžœžœ˜J˜J˜,J˜Kšœžœžœ ˜J˜—title™'Kšœžœžœžœ˜"šΟnœžœž œ2žœžœžœžœ!˜Jšœ%žœ˜*šœR˜R˜Kšœ<žœ˜BJšžœ˜ Kšœ˜—Kšœ˜—JšœI˜IJšœ;žœ žœ˜PJšžœ žœ˜Jšœ˜J™—š   œžœžœžœžœ˜>Kšžœžœžœ˜K•StartOfExpansion[]˜6Kšžœžœžœžœ!žœžœžœ˜EK˜K˜—Kšœ žœžœ&žœ˜EKšœžœžœ˜$š œžœžœžœžœžœ,žœ˜—Kšœ˜Kšœžœ˜,K˜šžœžœž˜KšœRžœ˜WKšœUžœ˜ZKšœ3žœ˜8Kšœ8žœ˜=Kšœ/žœ˜4šžœ˜ Kšžœ˜Kšœ˜——Jšœ˜Jšœ˜K–9[pa: ImagerPixelArrayDefs.PixelArray, layer: NAT _ 0]šœ=žœŸ-˜‰Kšœw˜wK–%[sampleMap: SampleMapOps.SampleMap]šœ0˜0K˜K˜—š   œžœžœžœžœ˜Qš ,œžœ˜6šœžœ ˜J˜!J˜+Jšœ ˜ —J–[name: ROPE]šœ4˜4Kšœ˜—Kšœ/žœžœ˜Lšž˜Kšœžœ ˜&—K˜K˜—š  œžœžœžœ žœžœžœ˜gš ,œžœ˜6Kšœžœ žœžœ˜ šžœžœ žœŸ2˜Ušœ žœ ˜J˜%J˜+Jšœ ˜ —Jšž˜—Kšœ'˜'K–-[name1: ROPE, name2: ROPE, name3: ROPE]šœ^˜^K˜—Kšœ/žœžœ˜Lšž˜Kšœžœ ˜&—K˜—code2š œžœ3˜MK–T[m: ImagerTransformation.Transformation, n: ImagerTransformation.Transformation]˜@K˜—š œžœ3˜JK–4[m: ImagerTransformation.Transformation, v: VEC]šœ žœ3™?K–4[m: ImagerTransformation.Transformation, v: VEC]šœ žœK™W–J[a: ImagerPixelMap.DeviceRectangle, b: ImagerPixelMap.DeviceRectangle]™Kšœžœ™-Kšœžœ™-Kšœžœžœ™HKšœžœžœ™GK™—K˜ –*[m: ImagerTransformation.Transformation]˜2KšœŸ+˜FK˜—K˜yKšœ1žœK˜Kšœ1žœK˜K˜—š  œžœ'žœžœ˜cK–J[a: ImagerPixelMap.DeviceRectangle, b: ImagerPixelMap.DeviceRectangle]šœ’˜’K˜—–y -- [self: ViewerClasses.Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE]šΟbœžœ˜:KšΠcku™uJšœ%žœ ˜7Jšœ"˜"J˜%Jšœ<˜šžœžœ˜K™"KšœŸ ˜"Kšœ˜—šžœ žœž˜šœžœ˜šžœž˜K˜K˜K˜K˜ K˜&Kšœ žœžœ žœ˜DKšœžœ˜Kšœžœ˜Kšœžœ˜!Kšœ žœ˜Kšžœ^žœ˜k—Kšœ˜—šœ$˜$šœ˜Kšœžœžœ9˜[KšœžœžœC˜eK˜Kšœ˜—Kšœ˜—Jšœžœ˜JšžœDžœ˜Q—Kšž˜—J˜J˜—š œ˜KšžœžœžœŸ˜HKšžœžœžœ.žœ˜]šž˜K˜—Kšœ˜—š œ˜KšžœžœžœŸ˜AK–;[self: ViewerClasses.Viewer _ NIL, info: REF ANY _ NIL]˜%Kšžœžœžœ/žœ˜_K˜šž˜K˜—Kšœ˜—š œ˜KšžœžœžœŸ˜6Kšžœžœžœ/žœ˜]K˜Kšœ˜—š œ˜šžœžœ˜!Kšžœžœžœ1žœ˜aKšžœžœ(Ÿ˜VKšœ˜—K˜K™K˜Kšœ0žœ˜6K˜Kšœ˜—š  œ˜šžœžœ˜KšœŸ˜+KšœŸ˜*Kšœ˜—Kšœ˜—K˜Kšœ žœžœ˜2š œ˜Kšžœžœžœ˜"š žœžœžœžœžœ˜7K™/Kšœ žœžœ˜=Kšœ˜—šžœ˜Kšœ)žœ˜3Kšœ˜—Kšœ˜——™ K˜*š  œžœ&žœžœžœ˜lšžœžœž˜Kšžœžœ$žœž˜AKšžœ˜—K˜K˜—head™Kšœ žœ˜!šœ˜K˜=K˜_Kšœ˜—šœ˜K˜.K˜tš  œžœ˜&K˜˜LKšœ žœžœžœ˜8K–elf: STREAM, r: ROPE]šžœ.˜0K–'[self: STREAM, abort: BOOL _ FALSE]šžœ˜K˜—K˜Kšœ˜—Kšœ žœžœ˜8šœ˜š œžœ˜š œžœ˜Kšœ žœžœg˜K˜—Kšœ žœ>˜LKšœ žœžœžœ˜0K–ldStream: STREAM _ NIL]š œ žœžœžœžœ˜!šž˜K–[self: STREAM]šžœ!žœžœžœ˜ZKšžœ˜—K–&[self: STREAM, close: BOOL _ TRUE]šœžœ˜2K˜/J–w[viewer: ViewerClasses.Viewer, hint: ViewerOps.PaintHint, clearClient: BOOL _ TRUE, whatChanged: REF ANY _ NIL]šœ?žœ˜FK˜—K˜Kšœ˜—Kšœžœžœ˜:šœ˜š œžœ˜Kšœ žœ@˜NK˜ZKšœ>˜>K˜—K˜Kšœ˜—Kšœžœžœ˜:šœ˜š œžœ˜š œžœ˜Kšœ žœžœg˜K˜—Kšœ žœ@˜NKšœ@žœžœ˜kK˜]K˜—K˜Kšœ˜—Kšœ žœžœ˜4šœ˜š œžœ˜Kšœ"žœ*˜OKšœžœžœ˜+šžœžœžœ ž˜K˜Kšžœ˜—K˜ZK˜˜DK˜—š œžœ)žœžœ˜bKšœžœ˜K˜(K˜(Kšžœžœ1˜FKšžœžœ1˜FK˜—š   œžœžœžœžœ˜aKšœΝ™Ν™Kšœžœ)™-—™KšœΠkmœ™Kšœ™—™&Kšœœ™K™—K–[a: REAL, m: RealOps.Mode _ [blank: 0, im: projective, nm: warning, round: rn, traps: (6)[TRUE, FALSE, TRUE, TRUE, TRUE, FALSE]]]šœ:˜:K–[a: REAL, m: RealOps.Mode _ [blank: 0, im: projective, nm: warning, round: rn, traps: (6)[TRUE, FALSE, TRUE, TRUE, TRUE, FALSE]]]šœ:˜:K–[a: REAL, m: RealOps.Mode _ [blank: 0, im: projective, nm: warning, round: rn, traps: (6)[TRUE, FALSE, TRUE, TRUE, TRUE, FALSE]]]šœ:˜:K–[a: REAL, m: RealOps.Mode _ [blank: 0, im: projective, nm: warning, round: rn, traps: (6)[TRUE, FALSE, TRUE, TRUE, TRUE, FALSE]]]šœ:˜:K˜—š œžœ8žœžœ˜Kšœ8žœ˜@Kšœ žœ˜KšœB˜BKšœB˜BKšœžœ˜Kšœžœ˜Kšœ%žœ0žœ˜pK˜—š œžœE˜YKšœ"˜"K˜ Kšœ>˜>K˜HKšœcŸΠbcŸ˜ˆKšœ=žœq˜±K˜—š œ˜K™/Kšœžœžœžœ%˜8Kšœžœ)žœžœ˜_š žœžœžœžœžœžœž˜;Kšœ'žœ˜>Kšž˜—K˜K˜—Mšœžœžœ˜š œžœ˜šœ,žœ!˜PJšœ˜Jšœ:˜:J˜J˜J˜Jšœžœ˜Jšœ˜Jšœ˜—J™J™J˜@J˜KšœŸ&˜