<> <> <> DIRECTORY ColorTrixMap, ColorTrixDispatch, Commander, Imager, ImagerFont, ImagerOps, ImagerTransformation, InterminalBackdoor, Menus, Process, Real, Rope, Terminal, TIPUser, ViewerClasses, ViewerEvents, ViewerOps; ColorTrixMapEditCommandImpl: CEDAR PROGRAM IMPORTS ColorTrixDispatch, ColorTrixMap, Imager, ImagerFont, ImagerOps, InterminalBackdoor, Menus, Process, Real, Terminal, TIPUser, ViewerEvents, ViewerOps ~ BEGIN Cmap: TYPE ~ ColorTrixMap.Cmap; CmEditData: TYPE ~ REF CmEditDataRec; CmEditDataRec: TYPE ~ RECORD [ cm, cmSave: Cmap, watch: BOOL _ FALSE, w, n, base, i0, i1, y0, y1: NAT _ 0, yMul, xScale, yScale: REAL _ 0.0, x: ARRAY[0..255] OF NAT _ ALL[0], y: ARRAY[0..2] OF ARRAY[0..255] OF NAT _ ALL[ALL[0]], bases: ARRAY[0..2] OF NAT _ ALL[0], viewer: ViewerClasses.Viewer _ NIL, close: ViewerEvents.EventRegistration _ NIL ]; Paint: ViewerClasses.PaintProc ~ { d: CmEditData _ NARROW[self.data]; Action: PROC ~ { IF whatChanged = NIL THEN {InitData[d]; DrawCmap[context, d, self.ww, self.wh]} ELSE Update[context, d]; Imager.SetFont[context, ImagerFont.Find["Xerox/TiogaFonts/Helvetica14"]]; FOR n: NAT IN[0..2] DO Imager.SetXYI[context, self.ww-50, 10+d.bases[n]]; Imager.ShowRope[context, SELECT n FROM 0 => "red", 1 => "green", ENDCASE => "blue"]; Imager.MaskRectangleI[context, 0, d.bases[n], self.ww, 1]; ENDLOOP; }; ImagerOps.DoWithBuffer[context, Action, 0, 0, self.ww, self.wh] }; InitData: PROC [d: CmEditData] ~ { x: REAL _ 0; xInc: REAL _ d.viewer.cw/255.0; compH: NAT _ d.viewer.ch/3; d.w _ Real.RoundI[xInc]; d.yMul _ compH/255.0; d.xScale _ IF d.viewer.cw > 1.0 THEN 255.0/(d.viewer.cw-1) ELSE 0.0; d.yScale _ IF d.yMul # 0.0 THEN 1.0/d.yMul ELSE 0.0; FOR i: NAT IN[0..255] DO d.x[i] _ Real.RoundI[x]; x _ x+xInc; ENDLOOP; FOR n: NAT IN[0..2] DO y: INTEGER _ d.bases[n] _ (2-n)*compH; FOR i: NAT IN[0..255] DO d.y[n][i] _ Real.RoundI[y+d.yMul*d.cm[n][i]]; ENDLOOP; ENDLOOP; TRUSTED {IF NOT d.watch THEN Process.Detach[FORK CmWatch[d]]}; }; CompIndex: PROC [d: CmEditData, y: INTEGER] RETURNS [NAT] ~ INLINE { RETURN[SELECT y FROM > d.bases[0] => 0, > d.bases[1] => 1, ENDCASE => 2]; }; Notify: ViewerClasses.NotifyProc ~ { d: CmEditData _ NARROW[self.data]; mouse: TIPUser.TIPScreenCoords _ NARROW[input.first]; SELECT input.rest.first FROM $move => { d.n _ CompIndex[d, mouse.mouseY]; d.base _ d.bases[d.n]; d.i0 _ d.i1 _ Real.RoundI[mouse.mouseX*d.xScale]; d.y0 _ d.y1 _ mouse.mouseY-d.bases[d.n]; }; $movingRed => { IF d.n # CompIndex[d, mouse.mouseY] THEN RETURN; -- deny component transition d.i0 _ d.i1; d.y0 _ d.y1; d.i1 _ Real.RoundI[mouse.mouseX*d.xScale]; d.y1 _ mouse.mouseY-d.base; }; $moveBlue => { IF d.n # CompIndex[d, mouse.mouseY] THEN RETURN; -- deny component transition d.i0 _ d.i1; d.y0 _ d.y1; d.i1 _ Real.RoundI[mouse.mouseX*d.xScale]; d.y1 _ mouse.mouseY-d.base; }; ENDCASE => RETURN; ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: d, clearClient: FALSE]; }; Update: PROC [cxt: Imager.Context, d: CmEditData] ~ { i0, i1: NAT; y, dy, imy, imdy: REAL; IF d.i1 > d.i0 THEN {i0 _ d.i0; i1 _ d.i1; y _ d.yScale*d.y0; imy _ d.base+d.y0} ELSE {i0 _ d.i1; i1 _ d.i0; y _ d.yScale*d.y1; imy _ d.base+d.y1}; Imager.SetColor[cxt, Imager.white]; FOR i: NAT IN[i0..i1] DO Imager.MaskRectangleI[cxt, d.x[i], d.y[d.n][i], d.w, 1]; ENDLOOP; Imager.SetColor[cxt, Imager.black]; dy _ IF i0 = i1 THEN 0.0 ELSE d.yScale*Real.Float[d.y1-d.y0]/(d.i1-d.i0); imdy _ d.yMul*dy; FOR i: NAT IN[i0..i1] DO d.cm[d.n][i] _ Real.RoundI[y]; ColorTrixMap.WriteEntry[i, d.cm[0][i], d.cm[1][i], d.cm[2][i]]; Imager.MaskRectangleI[cxt, d.x[i], d.y[d.n][i] _ Real.Round[imy], d.w, 1]; imy _ imy+imdy; y _ y+dy; ENDLOOP; }; CmEdit: PUBLIC Commander.CommandProc ~ { cm: Cmap _ ColorTrixMap.Read[]; data: CmEditData _ NEW[CmEditDataRec _ [cm: cm, cmSave: ColorTrixMap.Copy[cm]]]; outer: ViewerClasses.Viewer _ ViewerOps.CreateViewer[ flavor: $CmEdit, paint: FALSE, info: [ data: data, name: "Color Map Editor", openHeight: 300+24, iconic: TRUE, column: right, menu: Menus.CreateMenu[], scrollable: FALSE]]; Menus.AppendMenuEntry[outer.menu, Menus.CreateEntry["Undo", Undo, data,, FALSE, ]]; Menus.AppendMenuEntry[outer.menu, Menus.CreateEntry["Read", Read, data,, FALSE, ]]; data.viewer _ outer; data.close _ ViewerEvents.RegisterEventProc[proc: Close, event: close, filter: outer]; ViewerOps.AddProp[outer, $CmEdit, data]; ViewerOps.OpenIcon[outer]; }; DrawCmap: PROC [context: Imager.Context, d: CmEditData, w, h: INTEGER] ~ { Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, 0, 0, w, h]; Imager.SetColor[context, Imager.black]; FOR n: NAT IN[0..2] DO b: NAT _ d.bases[n]; FOR i: NAT IN[0..255] DO Imager.MaskRectangleI[context, d.x[i], Real.RoundI[b+d.yMul*d.cm[n][i]], d.w, 1]; ENDLOOP; ENDLOOP; }; NewCmap: PROC [d: CmEditData] ~ { d.cmSave _ ColorTrixMap.Copy[d.cm _ ColorTrixMap.Read[]]; Terminal.WaitForBWVerticalRetrace[InterminalBackdoor.terminal]; ViewerOps.PaintViewer[d.viewer, client, FALSE, NIL]; }; Read: Menus.ClickProc ~ { NewCmap[NARROW[clientData, CmEditData]]; }; Undo: Menus.ClickProc ~ { ColorTrixMap.Write[NARROW[clientData, CmEditData].cmSave]; }; CmWatch: PROC [d: CmEditData] ~ { d.watch _ TRUE; WHILE d.watch DO ColorTrixMap.WaitTilNew[]; NewCmap[d]; ENDLOOP; }; Destroy: ViewerClasses.DestroyProc ~ { data: CmEditData _ NARROW[self.data]; ViewerEvents.UnRegisterEventProc[proc: data.close, event: close]; data.watch _ FALSE; }; Close: ViewerEvents.EventProc ~ { data: CmEditData _ NARROW[ViewerOps.FetchProp[viewer, $CmEdit]]; IF data # NIL THEN data.watch _ FALSE; }; <> ViewerOps.RegisterViewerClass[$CmEdit, NEW[ViewerClasses.ViewerClassRec _ [ destroy: Destroy, paint: Paint, notify: Notify, tipTable: TIPUser.InstantiateNewTIPTable["CmEdit.TIP"]]]]; ColorTrixDispatch.RegisterCmOp["Edit", CmEdit, "Cm Edit: interactively edit the colormap."]; END.