~ {
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];
};
$moving => {
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: 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"]]]];
Commander.Register["CmEdit", CmEdit, "\nEdit the color display's color map."];
}.