<> <> <> <> <> DIRECTORY Imager, ImagerBasic USING [PixelArray, PixelBufferRep, PixelBuffer, DeviceRectangle, ColorRep, PathMapType], ImagerTransform USING [InverseTransform, Concat, Transform, Translate, Rotate], ImagerPixelMaps USING [PixelMap, DeviceRectangle, Create, ShiftMap, Clear, Trim, Copy, Window], ImagerMasks USING [PixelArrayFromPixelMap], Vector USING [VEC], Cubic USING [Bezier], FitJaM USING [defaultFitState, InitProc, RegisterInit], FitState USING [ResetData, AddSample, NewContour, NextContour, AddLink, EnumerateLinks, Handle], FS USING [Error, StreamOpen], Real USING [RoundLI], Scaled USING [FromReal, zero], SampledColors USING[bitmap, intensity, PixelArrayFromAIS, DrawImage], UFFileManager USING[KeyOf], UFPressFontReader USING[Size, Range, Family, Face, Resolution, GetCharInfo, GetCharRaster, CharInfo, GetCharOutline, FontKey, NumberOfFontsInFile], Font USING [Key, Create, FONT, Box, Pair, FontBoundingBox, WidthVector], Outline, JaMImager USING[Painter], JaM, Rope USING [ROPE, Fetch, FromChar], Basics USING [LowByte, LowHalf], AIS USING [Error], IO USING [STREAM, Close], SDtoSF USING [PathToStream]; ContourJaM: CEDAR PROGRAM IMPORTS Imager, SampledColors, JaMImager, AIS, JaM, Rope, FitState, ImagerTransform, Outline, FS, FitJaM, Real, Scaled, UFFileManager, UFPressFontReader, Font, ImagerPixelMaps, ImagerMasks, Basics, SDtoSF, IO EXPORTS = BEGIN acFont: InternalFont; sdFont: RECORD [font: Font.FONT, fontKey: UFPressFontReader.FontKey, scale: REAL]; sfFont: IO.STREAM; pixels: ImagerBasic.PixelArray; colorOperator: ATOM; outline: Outline.Handle _ NEW[Outline.Rec]; OpenAIS: PROCEDURE[state: JaM.State] = { ENABLE AIS.Error =>{JaM.ExecuteRope[state, "(AIS file error) .print" ! JaM.Stop => CONTINUE]; CONTINUE}; [pixels, colorOperator] _ SampledColors.PixelArrayFromAIS[JaM.PopRope[state]]; outline.tValue _ 128.5; outline.border _ 255; SetWindowFromPixels[]; }; OpenSF: PROC [state: JaM.State] = { sfFont _ FS.StreamOpen[JaM.PopRope[state], $append! FS.Error => IF error.group=user THEN JaM.ExecuteRope[state, "(Invalid file name) .print" ! JaM.Stop => CONTINUE]]; }; CloseSF: PROC [state: JaM.State] = {IO.Close[sfFont]}; WriteSFChar: PROC [state: JaM.State] = { s: REAL _ JaM.PopReal[state]; -- pointsize/resolution to make it a "1 point font" char: CHAR _ Rope.Fetch[JaM.PopRope[state],0]; PathMap: ImagerBasic.PathMapType = { fitState: FitState.Handle _ NARROW[data]; newContour: PROC[x,y: REAL] = {move[[s*x,s*y]]}; newCubic: PROC[c: Cubic.Bezier] = { curve[[s*c.b1.x, s*c.b1.y], [s*c.b2.x, s*c.b2.y], [s*c.b3.x, s*c.b3.y]]}; FitState.EnumerateLinks[fitState, newContour, newCubic]; }; SDtoSF.PathToStream[ dest: sfFont, pathMap: PathMap, pathData: FitJaM.defaultFitState, family: acFont.family, face: "M R R", charCode: VAL[char], widthx: acFont.charRep[char].fWidth, widthy: acFont.charRep[char].sWidth]; }; FontNotFound: SIGNAL = CODE; OpenACFont: PROCEDURE[state: JaM.State] = { ENABLE FontNotFound =>{JaM.ExecuteRope[state, "(File not found or file not an AC font) .print" ! JaM.Stop => CONTINUE]; CONTINUE}; acFont _ LoadAC[JaM.PopRope[state]]; JaM.PushRope[state,"A"]; GetACChar[state]; }; OpenSDFont: PROCEDURE[state: JaM.State] = { ENABLE FS.Error =>{JaM.ExecuteRope[state, "(file not found) .print" ! JaM.Stop => CONTINUE]; CONTINUE}; scale: REAL _ JaM.PopReal[state]; name: Rope.ROPE _ JaM.PopRope[state]; font: Font.FONT _ Font.Create[name, Imager.Scale[scale]]; sdFont _ [font, [font.graphicsKey, 0],scale]; }; GetACChar: PROCEDURE[state: JaM.State] = { IF acFont=NIL THEN { JaM.ExecuteRope[state, "(Please open a font) .print"! JaM.Stop => CONTINUE]; RETURN}; pixels _ acFont.charRep[Rope.Fetch[JaM.PopRope[state],0]].pixels; SetWindowFromPixels[]; colorOperator _ SampledColors.bitmap; outline.tValue _ 1; outline.border _ 0; }; GetSDChar: PROC [state: JaM.State] ~ { IF sdFont.font=NIL THEN { JaM.ExecuteRope[state, "(Please open a font) .print"! JaM.Stop => CONTINUE]; RETURN}; pixels _ PAFromSDChar[Rope.Fetch[JaM.PopRope[state],0]]; SetWindowFromPixels[]; colorOperator _ SampledColors.bitmap; outline.tValue _ 1; outline.border _ 0; }; ShowWindow: PROCEDURE[state: JaM.State] = { path: Imager.Trajectory _ Imager.MoveTo[[outline.xStart, outline.yStart]]; Paint: PROC[dc: Imager.Context] = { Imager.MaskStrokeClosed[dc, path, 0]; }; path _ Imager.LineTo[path, [outline.xStart, outline.yStart+outline.yPixels]]; path _ Imager.LineTo[path, [outline.xStart+outline.xPixels, outline.yStart+outline.yPixels]]; path _ Imager.LineTo[path, [outline.xStart+outline.xPixels, outline.yStart]]; JaMImager.Painter[Paint, state]; }; ResetWindow: PROCEDURE[state: JaM.State] = { SetWindowFromPixels[]; }; SetWindowFromPixels: PROC = { --find an upright box in client space sx, sy, ex, ey: REAL; [[sx, sy]] _ ImagerTransform.Transform[[0,0],pixels.m]; [[ex,ey]] _ ImagerTransform.Transform[[pixels.xPixels,pixels.yPixels],pixels.m]; outline.xStart _ Real.RoundLI[MIN[sx,ex]]; outline.xPixels _ Real.RoundLI[MAX[sx,ex]]-outline.xStart; outline.yStart _ Real.RoundLI[MIN[sy,ey]]; outline.yPixels _ Real.RoundLI[MAX[sy,ey]]-outline.yStart; }; <> InternalFont: TYPE ~ REF InternalFontRep; InternalFontRep: TYPE ~ RECORD [ family: Rope.ROPE _ NIL, face: [0..255] _ 0, bitsPerEmQuad: REAL _ 0, charRep: ARRAY CHAR OF InternalCharRep ]; InternalCharRep: TYPE ~ RECORD [ fWidth, sWidth: REAL, pixels: ImagerBasic.PixelArray ]; LoadAC: PROC [fileName: Rope.ROPE] RETURNS [internalFont: InternalFont] ~ TRUSTED { fileKey: Font.Key _ UFFileManager.KeyOf[fileName]; sizeInMeters: REAL; bc, ec: CHAR; IF UFPressFontReader.NumberOfFontsInFile[fileKey] = 0 THEN SIGNAL FontNotFound; [bc, ec] _ UFPressFontReader.Range[[fileKey, 0]]; sizeInMeters _ UFPressFontReader.Size[[fileKey, 0]]; internalFont _ NEW [InternalFontRep]; internalFont.family _ UFPressFontReader.Family[[fileKey, 0]]; internalFont.face _ UFPressFontReader.Face[[fileKey, 0]]; internalFont.bitsPerEmQuad _ sizeInMeters*UFPressFontReader.Resolution[[fileKey, 0]].xRes/0.0254; FOR char: CHAR IN [bc..ec] DO info: UFPressFontReader.CharInfo _ UFPressFontReader.GetCharInfo[[fileKey, 0], char]; pixelArray: ImagerBasic.PixelArray _ UFPressFontReader.GetCharRaster[[fileKey, 0], char]; internalFont.charRep[char] _ [ fWidth: info.widthX*internalFont.bitsPerEmQuad, sWidth: -info.widthY*internalFont.bitsPerEmQuad, pixels: pixelArray ]; ENDLOOP; }; Window: PROCEDURE[state: JaM.State] = { y2: REAL _ JaM.PopReal[state]; x2: REAL _ JaM.PopReal[state]; y1: REAL _ JaM.PopReal[state]; x1: REAL _ JaM.PopReal[state]; <> dx1, dx2, dy1, dy2: REAL; IF y2> <> [[dx1, dy1]] _ ImagerTransform.InverseTransform[[x1, y1],clientToView]; [[dx2, dy2]] _ ImagerTransform.InverseTransform[[x2, y2],clientToView]; outline.xStart _ Real.RoundLI[dx1]; outline.yStart _ Real.RoundLI[dy1]; outline.xPixels _ Real.RoundLI[dx2-dx1]; outline.yPixels _ Real.RoundLI[dy2-dy1]; }; GetValue: PROCEDURE[state: JaM.State] = { y: REAL _ JaM.PopReal[state]; x: REAL _ JaM.PopReal[state]; dx,dy: REAL; v: INT; [[dx, dy]] _ ImagerTransform.InverseTransform[[x, y],clientToView]; v _ Get[state,Real.RoundLI[dx],Real.RoundLI[dy]]; JaM.PushInt[state,v]; }; debug: BOOLEAN _ FALSE; buffer: ImagerBasic.PixelBuffer _ NEW[ImagerBasic.PixelBufferRep[1]]; Get: PROC[client: REF, x,y: INT] RETURNS[INT] = { Paint: PROC[dc: Imager.Context] = { IF buffer[0]=0 THEN RETURN; --binary case IF buffer[0] <= outline.tValue THEN { Imager.SetColor[dc, Imager.black]; Imager.MaskRectangle[dc,x,y,1,1]; }; }; s, f: REAL; [[s, f]] _ ImagerTransform.InverseTransform[[x, y],pixels.m]; pixels.get[pixels, buffer,1, 0, Scaled.FromReal[s], Scaled.FromReal[f], Scaled.zero, Scaled.zero]; IF debug THEN JaMImager.Painter[Paint, NARROW[client]]; RETURN[buffer[0]]; }; clientToView: Imager.Transformation _ Imager.Rotate[0]; ShowPA: PROCEDURE [state: JaM.State]= { Paint: PROC[dc: Imager.Context] = { SampledColors.DrawImage[dc,pixels,colorOperator]; }; JaMImager.Painter[Paint, state]; }; paint: BOOLEAN _ TRUE; NewEdge: PROCEDURE [client: REF, x0,y0,x1,y1: REAL] RETURNS [abort: BOOLEAN _ FALSE] = { state: JaM.State _ NARROW[client]; Paint: PROC[dc: Imager.Context] = { do: PROC = { <> Imager.SetColor[dc, Imager.black]; Imager.MaskVector[dc,[x0,y0],[x1,y1]]; }; Imager.DoSaveAll[dc, do]; }; IF paint THEN JaMImager.Painter[Paint, state]; RETURN[JaM.GetAbort[state]]; }; nContours: INT; OutlineEdge: PROC [state: JaM.State] = { outline.client _ state; nContours _ Outline.OutlineEdge[outline]; JaM.PushInt[state, nContours]; }; OutlineBlackCenter: PROC [state: JaM.State] = { outline.client _ state; nContours _ Outline.OutlineBlackCenter[outline]; JaM.PushInt[state, nContours]; }; OutlineBlackEdge: PROC [state: JaM.State] = { outline.client _ state; nContours _Outline.OutlineBlackEdge[outline]; JaM.PushInt[state, nContours]; }; ODrawContour: PROCEDURE[state: JaM.State] = { ODrawContourNumber[state, JaM.PopInt[state]]; }; ODrawAllContours: PROCEDURE[state: JaM.State] = { FOR cn: INT IN[0..nContours) DO ODrawContourNumber[state, cn]; ENDLOOP; }; ODrawContourNumber: PROCEDURE [state: JaM.State, cn: INT] = { Paint: PROC[dc: Imager.Context] = { x0, y0: REAL; first: BOOLEAN _ TRUE; newPt: PROC[x,y: REAL] = { IF first THEN first _ FALSE ELSE Imager.MaskVector[dc, [x0, y0], [x, y]]; x0 _ x; y0 _ y; }; Outline.GetOutline[outline.data, newPt, cn]; }; JaMImager.Painter[Paint, state]; }; PAFromSDChar: PROC [char: CHAR] RETURNS [pa: ImagerBasic.PixelArray] ~ { bb: Font.Box _ Font.FontBoundingBox[sdFont.font]; widthVector: Font.Pair _ Font.WidthVector[sdFont.font, char]; dbb: ImagerPixelMaps.DeviceRectangle _ [Real.RoundLI[-bb.ymax], Real.RoundLI[+bb.xmin], Real.RoundLI[bb.ymax]-Real.RoundLI[bb.ymin], Real.RoundLI[bb.xmax]-Real.RoundLI[bb.xmin]]; pixelMap: ImagerPixelMaps.PixelMap _ ImagerPixelMaps.Create[0, [0, 0, dbb.sSize+4, dbb.fSize+4]]; context: Imager.Context _ Imager.Create[$LFDisplay, NEW[ImagerPixelMaps.PixelMap _ pixelMap]]; pixelMap _ pixelMap.ShiftMap[dbb.sMin-2, dbb.fMin-2]; context.state.T _ Imager.Translate[2-bb.xmin, 2+dbb.sMin+dbb.sSize]; context.SetFont[sdFont.font]; pixelMap.Clear; context.SetXY[[0,0]]; context.ShowChar[char]; pixelMap _ pixelMap.Trim[0]; <> dbb _ pixelMap.Window[]; pixelMap _ pixelMap.ShiftMap[-dbb.sMin, -dbb.fMin].Copy; --for PixelArrayFromPixelMap pa _ ImagerMasks.PixelArrayFromPixelMap[pixelMap]; pa.m _ ImagerTransform.Concat[ pre: ImagerTransform.Translate[dbb.sMin, dbb.fMin], post: ImagerTransform.Rotate[-90]]; }; SetSDContour: PROC [state: JaM.State] = { s: REAL _ sdFont.scale; --sdfonts are all 1 point tall bezier: Cubic.Bezier; b0: Vector.VEC _ [0,0]; move: PROCEDURE [x, y: REAL] = { FitState.NewContour[FitJaM.defaultFitState]; b0 _ [s*x,s*y]; }; line: PROCEDURE [x, y: REAL] = { x _ x*s; y _ y*s; bezier _ [b0, [(2*b0.x+x)/3, (2*b0.y+y)/3], [(b0.x+2*x)/3, (b0.y+2*y)/3], [x,y]]; FitState.AddLink[FitJaM.defaultFitState, bezier]; b0 _ bezier.b3; }; curve: PROCEDURE [x1, y1, x2, y2, x3, y3: REAL] ={ --b1.x, b1.y, b2.x, b2.y, b3.x, b3.y bezier _ [b0, [x1*s, y1*s], [x2*s, y2*s], [x3*s, y3*s]]; FitState.AddLink[FitJaM.defaultFitState, bezier]; b0 _ bezier.b3; }; draw: PROCEDURE = { FitState.NextContour[FitJaM.defaultFitState]; }; UFPressFontReader.GetCharOutline[sdFont.fontKey, Rope.Fetch[JaM.PopRope[state],0], move, line, curve, draw]; }; OSetSamples: PROC [state: JaM.State] = { cn: INTEGER _ JaM.PopInt[state]; OSetSamplesNumber[cn]; }; OSetAllSamples: PROC [state: JaM.State] = { FOR cn: INT IN[0..nContours) DO OSetSamplesNumber[cn]; IF cn#nContours-1 THEN FitState.NewContour[FitJaM.defaultFitState]; ENDLOOP; }; OSetSamplesNumber: PROCEDURE [cn: INT] = { newPt: PROC[x,y: REAL] = { FitState.AddSample[FitJaM.defaultFitState, x,y]; }; FitState.ResetData[FitJaM.defaultFitState, samples]; Outline.GetOutline[outline.data, newPt, cn]; }; SetOutlineT: PROCEDURE[state: JaM.State] = {outline.tValue _ JaM.PopReal[state]}; CVChar: PROCEDURE[state: JaM.State] = { c: CHAR _ VAL[Basics.LowByte[Basics.LowHalf[JaM.PopInt[state]]]]; JaM.PushRope[state, Rope.FromChar[c]]; }; Init: FitJaM.InitProc = { JaM.Register[state,"Contour.openSF",OpenSF]; --open the SF output file JaM.Register[state,"Contour.closeSF",CloseSF]; --close the SF output file JaM.Register[state,"Contour.writeSFChar",WriteSFChar]; --write the current contour JaM.Register[state,"Contour.openAIS",OpenAIS]; --open the ais file (will close current file) JaM.Register[state,"Contour.openACFont", OpenACFont]; --(name) => open a font file JaM.Register[state,"Contour.openSDFont", OpenSDFont]; --(name) size => open a font file JaM.Register[state,"Contour.showPA",ShowPA]; --use Imager to display the current file JaM.Register[state,"Contour.getACChar", GetACChar]; --(char) => sets up a character for outlining JaM.Register[state,"Contour.getSDChar", GetSDChar]; --(char) => scan converts an SD char at the current size and sets it up for outlining JaM.Register[state,"Contour.setSDContour", SetSDContour]; --(char) => creates contour from an sd char JaM.Register[state,"Contour.windowPA", Window]; --x1 y1 x2 y2 => Set a window on the ais image JaM.Register[state,"Contour.showWindow", ShowWindow]; --draws outline of window JaM.Register[state,"Contour.resetWindow", ResetWindow]; --resets window to the size of the pixel array JaM.Register[state,"Contour.getValue", GetValue]; --x, y => returns the value in the ps JaM.Register[state,"Outline.tvalue",SetOutlineT]; -- Outline threshhold value JaM.Register[state,".outline",OutlineEdge]; --find the contours JaM.Register[state,".outlineBlackCenter",OutlineBlackCenter]; --find the edge through the black region. Makes sense for binary files. JaM.Register[state,".outlineBlackEdge",OutlineBlackEdge]; --find the edge around the black region. Makes sense for binary files. JaM.Register[state,"Outline.drawContour",ODrawContour]; --number .drawContour JaM.Register[state,"Outline.drawAllContours",ODrawAllContours]; --.drawAllContour JaM.Register[state,"Outline.setsa",OSetSamples]; --send numbered contour's samples to FitState JaM.Register[state,"Outline.setallsa",OSetAllSamples]; --send all contours to FitState JaM.Register[state,".cvchar",CVChar]; --Convert a number to a character }; outline.get _ Get; outline.newEdge _ NewEdge; FitJaM.RegisterInit[$ContourJaM, Init]; END.