CtPaintCommandImpl:
CEDAR
PROGRAM
IMPORTS Buttons, ChoiceButtons, Convert, CtBasic, CtDispatch, CtViewer, ImagerSample, IO, KeyNames, MessageWindow, Process, SF, ViewerOps, ViewerTools
Paint Command
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD [
cmd: CtDispatch.CmdHandle ¬ NIL,
pval: CARDINAL ¬ 100,
rgb: RGB ¬ [100, 100, 100],
size: INTEGER ¬ 25,
halfSize: INTEGER ¬ 12,
paintViewer: Viewer ¬ NIL,
parent: Viewer ¬ NIL,
pvalViewer: Viewer ¬ NIL,
sizeViewer: Viewer ¬ NIL,
redViewer: Viewer ¬ NIL,
greenViewer: Viewer ¬ NIL,
blueViewer: Viewer ¬ NIL,
functionViewer: Viewer ¬ NIL,
function: {paint, blur, incr} ¬ paint
];
ctPaintUsage:
ROPE ~ "Ct Paint: interactive painting on a viewer.";
CtPaint: CtDispatch.CtProc ~ {
v: Viewer ¬ ViewerOps.FindViewer["Ct Paint"];
p: Data ¬ IF v = NIL THEN NEW[DataRep] ELSE NARROW[v.data];
p.cmd ¬ cmd;
IF v = NIL THEN TRUSTED {Process.Detach[FORK MakePainter[p]]};
affect ¬ [[0, 0], [0, 0]]; -- caveat painter!
CtViewer.RegisterMouse[p.paintViewer ¬ viewer, MouseProc, p];
};
MakePainter:
PROC [p: Data] ~ {
TextButton:
PROC [x, y, w:
NAT, name, text:
ROPE]
RETURNS [v: Viewer] ~ {
v ¬ ChoiceButtons.BuildTextPrompt[p.parent, x, y, name, text,, w, p].textViewer;
};
ViewerOps.OpenIcon[p.parent ¬ ViewerOps.CreateViewer[
flavor: $CtPaint,
info: [openHeight: 34, name: "Ct Paint", data: p, column: right, iconic: TRUE]]];
p.sizeViewer ¬ TextButton[0, 3, 40, "size:", "25"];
p.pvalViewer ¬ TextButton[85, 3, 40, "p:", "100"];
p.redViewer ¬ TextButton[150, 3, 40, "r:", "100"];
p.greenViewer ¬ TextButton[215, 3, 40, "g:", "100"];
p.blueViewer ¬ TextButton[280, 3, 40, "b:", "100"];
p.functionViewer ¬ Buttons.Create[
info: [parent: p.parent, name: "paint", wx: 360, wy: 3], proc: Toggle, clientData: p];
};
MouseProc: CtViewer.MouseProc ~ {
p: Data ¬ NARROW[clientData];
IF mouse.state = down
AND mouse.button = left
THEN CtViewer.DoWithViewer[p.paintViewer, Paint, p];
};
Paint: CtViewer.ViewerProc ~ {
GetVal:
PROC [v: Viewer]
RETURNS [i:
CARDINAL] ~ {
i ¬ Convert.IntFromRope[ViewerTools.GetContents[v] ! Convert.Error => GOTO Bad];
EXITS Bad=>{MessageWindow.Append["bad value",TRUE];MessageWindow.Blink[]};
};
Poll: CtViewer.PollProc ~ {
IF p.parent.destroyed
THEN RETURN[FALSE]
ELSE {
Blur:
PROC ~ {
PixelProc:
SAFE PROC [x, y:
NAT] ~ {
sum, sumR, sumG, sumB: CARDINAL ¬ 0;
FOR yy:
INTEGER
IN [y-1..y+1]
DO
FOR xx:
INTEGER
IN [x-1..x+1]
DO
IF maps.bpp = 24
THEN {
rgb: RGB ¬ CtBasic.GetRGBPixel[maps, xx, yy];
sumR ¬ sumR+rgb.r; sumG ¬ sumG+rgb.g; sumB ¬ sumB+rgb.b;
}
ELSE sum ¬ sum+CtBasic.GetBWPixel[maps[0].map, xx, yy];
ENDLOOP;
ENDLOOP;
IF maps.bpp = 24
THEN CtBasic.PutRGBPixel[maps, x, y, [sumR/9, sumG/9, sumB/9]]
ELSE CtBasic.PutBWPixel[maps[0].map, x, y, CARDINAL[sum/9]];
};
FOR
y:
NAT
IN
[y0..y1]
DO
FOR x: NAT IN [x0..x1] DO PixelProc[x, y]; ENDLOOP;
ENDLOOP;
};
Paint:
PROC ~ {
IF maps.bpp = 24
THEN CtBasic.PutRGBBox[maps, x0, y0, x1, y1, p.rgb]
ELSE ImagerSample.Fill[maps[0].map, [[y0, x0], [y1, x1]], p.pval];
};
Incr:
PROC ~ {
IF maps.bpp = 24
THEN p.rgb ¬ [
BYTE[(NAT[p.rgb.r]+1) MOD 255],
BYTE[(NAT[p.rgb.g]+2) MOD 255],
BYTE[(NAT[p.rgb.b]+3) MOD 255]]
ELSE
SELECT dir
FROM
positive => IF p.pval = 254 THEN dir ¬ negative ELSE p.pval ¬ p.pval+1;
negative => IF p.pval = 1 THEN dir ¬ positive ELSE p.pval ¬ p.pval-1;
ENDCASE;
Paint[];
};
x0: INTEGER ¬ MAX[maps.x, pos.x-p.halfSize];
y0: INTEGER ¬ MAX[maps.y, pos.y-p.halfSize];
x1: INTEGER ¬ MIN[maps.box.max.f, x0+p.size];
y1: INTEGER ¬ MIN[maps.box.max.s, y0+p.size];
IF x0 # prev.x
OR y0 # prev.y
THEN {
affect ¬ [SF.Min[affect.min, [y0, x0]], SF.Max[affect.max, [y1, x1]]];
SELECT p.function FROM paint => Paint[]; blur => Blur[]; ENDCASE => Incr[];
prev ¬ [x0, y0];
};
};
};
p: Data ¬ NARROW[clientData];
affect: SF.Box ¬ [[0, 0], [0, 0]];
prev: CtBasic.IntegerPair ¬ [-1, -1];
dir: {positive, negative} ¬ positive;
IF CtViewer.KeyDown[control]
THEN {
-- pick
pos: IntegerPair ¬ CtViewer.GetMousePos[viewer];
IF maps.bpp = 24
THEN {
rgb: RGB ¬ CtBasic.GetRGBPixel[maps, pos.x, pos.y];
ViewerTools.SetContents[p.redViewer, IO.PutFR1["%g", IO.card[rgb.r]]];
ViewerTools.SetContents[p.greenViewer, IO.PutFR1["%g", IO.card[rgb.g]]];
ViewerTools.SetContents[p.blueViewer, IO.PutFR1["%g", IO.card[rgb.b]]];
}
ELSE {
v: CARDINAL ¬ CtBasic.GetBWPixel[maps[0].map, pos.x, pos.y];
ViewerTools.SetContents[p.pvalViewer, IO.PutFR1["%g", IO.card[v]]];
};
};
p.rgb ¬ [GetVal[p.redViewer], GetVal[p.greenViewer], GetVal[p.blueViewer]];
p.halfSize ¬ (p.size ¬ GetVal[p.sizeViewer])/2;
p.pval ¬ GetVal[p.pvalViewer];
CtViewer.DoWhileMouseDown[viewer, Poll];
CtViewer.PutBuffer[viewer, maps, affect];
};
Destroy: ViewerClasses.DestroyProc ~ {
CtViewer.UnregisterMouse[NARROW[self.data, Data].paintViewer, MouseProc];
};
Toggle: Buttons.ButtonProc ~ {
p: Data ¬ NARROW[clientData];
p.function ¬ SELECT p.function FROM paint => blur, blur => incr, ENDCASE => paint;
Buttons.ReLabel[p.functionViewer,
SELECT p.function FROM paint => "paint", blur => "blur", ENDCASE => "incr"];
};
Start Code
ViewerOps.RegisterViewerClass[$CtPaint,
NEW[ViewerClasses.ViewerClassRec¬[destroy:Destroy]]];
CtDispatch.RegisterCtOp["Painting:", NIL, NIL];
CtDispatch.RegisterCtOp["Paint", CtPaint, ctPaintUsage];