<<>> <> <> <> <> DIRECTORY Buttons, CtBasic, CtDispatch, CtMisc, CtMod, CtViewer, Commander, G2dContour, G2dBasic, ImagerSample, IO, MessageWindow, Process, Real, Rope, RuntimeError, TypeScript, ViewerClasses, ViewerIO, ViewerOps, ViewerSpecs, ViewerTools; CtWarpCommandsImpl: CEDAR PROGRAM IMPORTS Buttons, CtBasic, CtDispatch, CtMisc, CtMod, CtViewer, G2dContour, G2dBasic, ImagerSample, IO, MessageWindow, Process, Real, RuntimeError, TypeScript, ViewerIO, ViewerOps, ViewerSpecs, ViewerTools ~ BEGIN <> IntegerPair: TYPE ~ CtBasic.IntegerPair; PixelArray: TYPE ~ CtBasic.PixelArray; SampleMaps: TYPE ~ CtBasic.SampleMaps; CtProc: TYPE ~ CtDispatch.CtProc; PairSequence: TYPE ~ G2dBasic.PairSequence; ROPE: TYPE ~ Rope.ROPE; Viewer: TYPE ~ ViewerClasses.Viewer; ViewerClassRec: TYPE ~ ViewerClasses.ViewerClassRec; <> Persp: TYPE ~ REF PerspRep; PerspRep: TYPE ~ RECORD [ parent: Viewer ¬ NIL, -- parent viewer image: Viewer ¬ NIL, -- the viewer to warp button: CtViewer.MouseButton ¬ left, box: ImagerSample.Box ¬ [[0, 0], [0, 0]], -- texture box texture: PixelArray ¬ NIL, -- array of chosen texture smooth: BOOL ¬ FALSE, -- anti-alias or not? index: NAT ¬ 0, -- index to warp quadilateral points points: ARRAY [0..4) OF IntegerPair -- warp quadrilateral ]; ctPerspWarpUsage: ROPE ~ "Ct PerspWarp: Warp a window into a quadrilateral."; CtPerspWarp: CtProc ~ { v: Viewer ¬ ViewerOps.FindViewer["Ct PerspWarp"]; d: Persp ¬ IF v = NIL THEN NEW[PerspRep] ELSE NARROW[v.data]; d.image ¬ viewer; IF v = NIL THEN TRUSTED {Process.Detach[FORK MakePersper[d]]}; affect ¬ [[0, 0], [0, 0]]; CtViewer.RegisterMouse[viewer, PerspMouse, d]; }; MakePersper: PROC [d: Persp] ~ { d.parent ¬ MakeViewer["Ct PerspWarp", $CtPerspWarp, 51, d]; ViewerTools.SetContents[ViewerTools.MakeNewTextViewer[ info: [scrollable: FALSE, parent: d.parent, border: FALSE, wx: 7, wy: 0, ww: 400, wh: 16]], "Left: pick texture, Middle: warp points, Right: restart"]; Buttons.SetDisplayStyle[Buttons.Create[ [parent: d.parent, name: "Warp-On ", wx: 3, wy: 20], PerspWarpTog, d], $WhiteOnBlack]; Buttons.SetDisplayStyle[Buttons.Create[ [parent: d.parent, name: "Smooth-Off ", wx: 70, wy: 20], SmoothTog, d], $BlackOnWhite]; }; PerspWarpTog: Buttons.ButtonProc ~ { d: Persp ¬ NARROW[clientData]; CtMisc.ButtonToggle[parent]; IF CtMisc.ButtonOn[parent] THEN CtViewer.RegisterMouse[d.image, PerspMouse, d] ELSE CtViewer.UnregisterMouse[d.image, PerspMouse]; }; SmoothTog: Buttons.ButtonProc ~ { d: Persp ¬ NARROW[clientData]; CtMisc.ButtonToggle[parent]; d.smooth ¬ CtMisc.ButtonOn[parent] }; PerspMouse: CtViewer.MouseProc ~ { d: Persp ¬ NARROW[clientData]; IF mouse.state # down THEN RETURN; IF (d.button ¬ mouse.button) = left THEN d.box ¬ CtViewer.GetBoundingBox[viewer, [mouse.pos.y, mouse.pos.x]]; CtViewer.DoWithViewer[viewer, DoPersp, d]; }; DoPersp: CtViewer.ViewerProc ~ { d: Persp ¬ NARROW[clientData]; CtViewer.DoWhileMouseDown[viewer, NIL]; SELECT d.button FROM left => d.texture ¬ CtBasic.PixelArrayFromSampleMap[ImagerSample.Clip[maps[0].map, d.box]]; middle => { d.points[d.index] ¬ CtViewer.GetMousePos[viewer]; IF (d.index ¬ (d.index+1) MOD 4) = 0 THEN [] ¬ CtMod.PerspWarp[d.texture, maps, d.smooth, d.points]; }; ENDCASE => CtBasic.TransferPixelArrayToMap[d.texture, maps[0].map]; }; PerspWarpDestroy: ViewerClasses.DestroyProc ~ { CtViewer.UnregisterMouse[NARROW[self.data, Persp].image, PerspMouse]; }; <> CoonsControl: TYPE ~ CtMod.CoonsControl; Coons: TYPE ~ REF CoonsRep; CoonsRep: TYPE ~ RECORD [ -- info for Coons mouse and warp procs flag: ATOM ¬ NIL, mouse: CtViewer.Mouse ¬ [], cmd: Commander.Handle ¬ NIL, -- Commander handle control: REF CoonsControl ¬ NIL, -- control during warping r, g, b, bw: PixelArray, -- source picture (8 or 24 bpp) ind: [0..4] ¬ 0, -- which curve are we editing? [0..4) curves: ARRAY [0..4) OF PairSequence, -- the four outline curves image: Viewer ¬ NIL, -- the viewer to warp parent: Viewer ¬ NIL, -- the parent viewer text: Viewer ¬ NIL, -- log's viewer, for feedback filterButton: Viewer ¬ NIL, -- button for filter mode lifeButton: Viewer ¬ NIL, -- to put CoonsWarp to sleep or wake up awake: BOOL ¬ TRUE, -- awake or asleep? s: IO.STREAM ¬ NIL -- log's output stream ]; ctCoonsWarpUsage: ROPE ~ "Ct CoonsWarp: Warp an outlined area into a rectangle."; CtCoonsWarp: CtProc ~ { v: Viewer ¬ ViewerOps.FindViewer["Ct CoonsWarp"]; d: Coons ¬ IF v = NIL THEN NEW[CoonsRep] ELSE NARROW[v.data]; affect ¬ [[0, 0], [0, 0]]; d.cmd ¬ cmd; d.image ¬ viewer; IF v = NIL THEN TRUSTED {Process.Detach[FORK MakeCoonser[d]]}; IF maps.bpp = 24 THEN { d.r ¬ CtBasic.PixelArrayFromSampleMap[maps[0].map]; d.g ¬ CtBasic.PixelArrayFromSampleMap[maps[1].map]; d.b ¬ CtBasic.PixelArrayFromSampleMap[maps[2].map]; } ELSE d.bw ¬ CtBasic.PixelArrayFromSampleMap[maps[0].map]; CtViewer.RegisterMouse[viewer, CoonsMouse, d]; }; MakeCoonser: PROC [d: Coons] ~ { d.parent ¬ MakeViewer["Ct CoonsWarp", $CtCoonsWarp, 120, d]; d.text ¬ TypeScript.Create[ [parent: d.parent, border: FALSE, wx: 0, wy: 0, ww: ViewerSpecs.openRightWidth, wh: 85]]; d.s ¬ ViewerIO.CreateViewerStreams[NIL, d.text].out; Buttons.SetDisplayStyle[d.lifeButton ¬ Buttons.Create[ [parent: d.parent, name: "AWAKE ", wx: 3, wy: 90], CoonsAwakeTog, d], $WhiteOnBlack]; [] ¬ Buttons.Create[[parent: d.parent, name: "Abort", wx: 50, wy: 90], CoonsAbort, d,,,, TRUE]; [] ¬ Buttons.Create[[parent: d.parent, name: "Restart", wx: 95, wy: 90], CoonsRestart, d]; [] ¬ Buttons.Create[[parent: d.parent, name: "Smooth", wx: 145, wy: 90], CoonsSmooth, d]; d.filterButton ¬ Buttons.Create[ [parent: d.parent, name: "Filter: point", wx: 240, wy: 90], CoonsFilter, d]; IO.PutRope[d.s, "\nPlease wait . . . "]; FOR i: INT IN [0..4) DO d.curves[i] ¬ NEW[G2dBasic.PairSequenceRep[64]]; ENDLOOP; -- grow? d.control ¬ NEW[CoonsControl]; IO.PutRope[d.s, "\nSTART DRAWING: L: draw outline, M: warp, R: restore original\n\n"]; }; CoonsSmooth: Buttons.ButtonProc ~ { d: Coons ¬ NARROW[clientData]; CoonsCurvesDraw[d, $Undraw]; FOR j: NAT IN [1..10] DO FOR i: NAT IN [0..4) DO [] ¬ G2dContour.SmoothPairs[d.curves[i], d.curves[i]]; ENDLOOP; ENDLOOP; CoonsCurvesDraw[d, $Draw]; }; CoonsRestart: Buttons.ButtonProc ~ {NARROW[clientData, Coons].ind ¬ 0}; CoonsAbort: Buttons.ButtonProc ~ {NARROW[clientData, Coons].control.abort ¬ TRUE}; CoonsAwakeTog: Buttons.ButtonProc ~ { d: Coons ¬ NARROW[clientData]; Buttons.ReLabel[d.lifeButton, IF d.awake ¬ NOT d.awake THEN "AWAKE" ELSE "ASLEEP"]; Buttons.SetDisplayStyle[d.lifeButton, IF d.awake THEN $WhiteOnBlack ELSE $BlackOnWhite]; IF d.awake THEN CtViewer.RegisterMouse[d.image, CoonsMouse, d] ELSE CtViewer.UnregisterMouse[d.image, CoonsMouse]; }; CoonsFilter: Buttons.ButtonProc ~ { d: Coons ¬ NARROW[clientData]; d.control.filter ¬ IF d.control.filter = point THEN box ELSE point; Buttons.ReLabel[d.filterButton, IF d.control.filter = point THEN "Filter: point" ELSE "Filter: box"]; }; CoonsWarpDestroy: ViewerClasses.DestroyProc ~ { d: Coons ¬ NARROW[self.data]; d.s ¬ d.cmd.out; d.control.abort ¬ TRUE; CtViewer.UnregisterMouse[d.image, CoonsMouse]; }; CoonsCurvesDrawAction: CtViewer.ViewerProc ~ { d: Coons ¬ NARROW[clientData]; FOR c: NAT IN [0..4) DO FOR i: NAT IN [0..d.curves[c].length) DO CoonsDotDraw[d, maps, Real.Fix[d.curves[c][i].x], Real.Fix[d.curves[c][i].y], d.flag]; ENDLOOP; ENDLOOP; }; CoonsCurvesDraw: PROC [d: Coons, flag: ATOM] ~ { d.flag ¬ flag; CtViewer.DoWithViewer[d.image, CoonsCurvesDrawAction, d]; }; CoonsDotDraw: PROC [d: Coons, maps: SampleMaps, x, y: NAT, flag: ATOM] ~ { x0: NAT ¬ x-1; x1: NAT ¬ x+1; y0: NAT ¬ y-1; y1: NAT ¬ y+1; SELECT flag FROM $Draw => SELECT maps.bpp FROM 8 => ImagerSample.Fill[maps[0].map, [[y0, x0], [y1, x1]], 255]; 24 => CtBasic.PutRGBBox[maps, x0, y0, x1, y1, [255, 255, 255]]; ENDCASE; $Undraw => SELECT maps.bpp FROM 8 => CtBasic.PixelArrayBoxToMap[d.bw, maps[0].map, x0, y0, x1, y1]; 24 => { CtBasic.PixelArrayBoxToMap[d.r, maps[0].map, x0, y0, x1, y1]; CtBasic.PixelArrayBoxToMap[d.g, maps[1].map, x0, y0, x1, y1]; CtBasic.PixelArrayBoxToMap[d.b, maps[2].map, x0, y0, x1, y1]; }; ENDCASE; ENDCASE; }; CoonsMouseAction: CtViewer.ViewerProc ~ { d: Coons ¬ NARROW[clientData]; SELECT d.mouse.button FROM middle => { -- warp d.control.abort ¬ FALSE; IF d.ind # 0 THEN Blink["Four boundary curves needed"] ELSE { CtMod.CoonsWarp[d.r, d.g, d.b, d.curves, maps, d.control]; IO.PutRope[d.s, " done!\n"]; }; }; right => -- restore original image SELECT maps.bpp FROM 8 => CtBasic.TransferPixelArrayToMap[d.bw, maps[0].map]; 24 => { CtBasic.TransferPixelArrayToMap[d.r, maps[0].map]; CtBasic.TransferPixelArrayToMap[d.g, maps[1].map]; CtBasic.TransferPixelArrayToMap[d.b, maps[2].map]; }; ENDCASE; left => { -- add point(s) AddPoint: PROC [pos: IntegerPair] ~ { d.curves[d.ind] ¬ G2dBasic.AddToPairSequence[d.curves[d.ind], [pos.x, pos.y] ! RuntimeError.BoundsFault => GOTO Bad]; -- record the point in curve array CoonsDotDraw[d, maps, pos.x, pos.y, $Draw]; -- draw dots on screen EXITS Bad => { Blink[IO.PutFR["Curve %g length exceeded at %g points", IO.int[d.ind], IO.int[d.curves[d.ind].length]]]; d.curves[d.ind].length ¬ 0; -- best can do: restart curve }; }; Poll: CtViewer.PollProc ~ {AddPoint[pos]}; CtViewer.UnregisterMouse[viewer, CoonsMouse]; IO.PutF1[d.s, "curve %g: ", IO.int[d.ind]]; -- assume just had a mouse-down d.curves[d.ind].length ¬ 0; AddPoint[d.mouse.pos]; CtViewer.DoWhileMouseDown[viewer, Poll]; IO.PutF1[d.s, "%g pts, ", IO.int[d.curves[d.ind].length]]; IF d.curves[d.ind].length < 2 THEN { Blink[IO.PutFR["Too few points (%g), redraw curve %g", IO.int[d.curves[d.ind].length], IO.int[d.ind]]]; d.curves[d.ind].length ¬ 0; -- best can do: restart curve IO.PutF1[d.s, "curve %g: ", IO.int[d.ind]]; } ELSE d.ind ¬ (d.ind+1) MOD 4; CtViewer.RegisterMouse[viewer, CoonsMouse, d]; }; ENDCASE; }; CoonsMouse: CtViewer.MouseProc ~ { d: Coons ¬ NARROW[clientData]; d.mouse ¬ mouse; CtViewer.DoWithViewer[viewer, CoonsMouseAction, d]; }; <> MakeViewer: PROC [name: ROPE, flavor: ATOM, openHeight: NAT, data: REF ANY] RETURNS [viewer: Viewer] ~ { viewer ¬ ViewerOps.CreateViewer[ flavor: flavor, info: [openHeight: openHeight, name: name, data: data, scrollable: FALSE, column: right, iconic: TRUE]]; ViewerOps.OpenIcon[viewer]; }; Blink: PROC [rope: ROPE] ~ { MessageWindow.Append[rope, TRUE]; MessageWindow.Blink[]; }; <> ViewerOps.RegisterViewerClass[ $CtPerspWarp, NEW[ViewerClassRec ¬ [destroy: PerspWarpDestroy]]]; ViewerOps.RegisterViewerClass[ $CtCoonsWarp, NEW[ViewerClassRec ¬ [destroy: CoonsWarpDestroy]]]; CtDispatch.RegisterCtOp["Warp:", NIL, NIL]; CtDispatch.RegisterCtOp["PerspWarp", CtPerspWarp, ctPerspWarpUsage]; CtDispatch.RegisterCtOp["CoonsWarp", CtCoonsWarp, ctCoonsWarpUsage]; END.