<> <> <> <<>> 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 ~ { <<[self: ViewerClasses.Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE]>> 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 = { <<[self: ViewerClasses.Viewer, op: ViewerClasses.ScrollOp, amount: INTEGER, shift: BOOL _ FALSE, control: BOOL _ FALSE] RETURNS [top: INTEGER, bottom: INTEGER]>> 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 = { <<[self: ViewerClasses.Viewer, op: ViewerClasses.HScrollOp, amount: INTEGER, shift: BOOL _ FALSE, control: BOOL _ FALSE] RETURNS [left: INTEGER, right: INTEGER]>> 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 = { <<[self: ViewerClasses.Viewer, input: LIST OF REF ANY]>> 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] }; <> 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 ]]; <> 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 ]]; <> 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 ]]; <> [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] ~ { <> <> <> <> <> <> <0):>> <> < (x - 0.5 - m.c)/m.a>> 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.