DIRECTORY Font USING [FONT], ImagerBasic USING [ColorRep, IntPair, IntRectangle, nullREAL, Pair, PixelArray, StrokeEnd, Transformation, Visibility], Rope USING [ROPE]; Imager: CEDAR DEFINITIONS = BEGIN Color: TYPE = REF ImagerBasic.ColorRep; ConstantColor: TYPE = REF ImagerBasic.ColorRep[constant]; Pair: TYPE = ImagerBasic.Pair; PixelArray: TYPE = ImagerBasic.PixelArray; StrokeEnd: TYPE = ImagerBasic.StrokeEnd; Transformation: TYPE = ImagerBasic.Transformation; Visibility: TYPE = ImagerBasic.Visibility; nullREAL: REAL = ImagerBasic.nullREAL; FONT: TYPE = Font.FONT; ROPE: TYPE = Rope.ROPE; RopeOrRefText: TYPE = REF; Error: ERROR[errorCode: ErrorCode]; ErrorCode: TYPE = ATOM; Context: TYPE = REF ContextRep; ContextRep: TYPE = RECORD[class: REF ANY, data: REF ANY, state: REF ANY]; Create: PROC[deviceType: ATOM, data: REF _ NIL] RETURNS [Context]; DoSave: PROC[context: Context, body: PROC]; DoSaveAll: PROC[context: Context, body: PROC]; SetPriorityImportant: PROC[context: Context, priorityImportant: BOOL]; pointsToMeters: Transformation; -- printer's points, 72.27 to the inch micasToMeters: Transformation; ConcatT: PROC[context: Context, m: Transformation]; TranslateT: PROC[context: Context, x, y: REAL]; RotateT: PROC[context: Context, a: REAL]; ScaleT: PROC[context: Context, s: REAL]; Scale2T: PROC[context: Context, sx, sy: REAL]; Move: PROC[context: Context]; Trans: PROC[context: Context]; SetXY: PROC[context: Context, p: Pair]; IntegerSetXY: PROC[context: Context, x, y: INTEGER]; SetXYRel: PROC[context: Context, v: Pair]; IntegerSetXYRel: PROC[context: Context, x, y: INTEGER]; SetXRel: PROC[context: Context, x: REAL]; IntegerSetXRel: PROC[context: Context, x: INTEGER]; SetYRel: PROC[context: Context, y: REAL]; IntegerSetYRel: PROC[context: Context, y: INTEGER]; MakeGray: PROC[f: REAL] RETURNS[ConstantColor]; black: ConstantColor; -- = MakeGray[1] white: ConstantColor; -- = MakeGray[0] SetColor: PROC[context: Context, color: Color]; Trajectory: TYPE = REF TrajectoryRep; TrajectoryRep: TYPE = PRIVATE RECORD[ prev: Trajectory, -- trajectory preceding this segment lp: Pair, -- the last point variant: SELECT tag: * FROM move => [], -- begin new trajectory at lp line => [], -- straight line segment, endpoints [prev.lp, lp] curve => [p1, p2: Pair], -- cubic curve segment, Bezier control points [prev.lp, p1, p2, lp] conic => [p1: Pair, r: REAL], -- conic section segment; see Full Interpress ENDCASE ]; LastPoint: PROC[t: Trajectory] RETURNS[Pair]; MoveTo: PROC[p: Pair] RETURNS[Trajectory]; LineTo: PROC[t: Trajectory, p: Pair] RETURNS[Trajectory]; LineToX: PROC[t: Trajectory, x: REAL] RETURNS[Trajectory]; LineToY: PROC[t: Trajectory, y: REAL] RETURNS[Trajectory]; CurveTo: PROC[t: Trajectory, p1, p2, p3: Pair] RETURNS[Trajectory]; ConicTo: PROC[t: Trajectory, p1, p2: Pair, r: REAL] RETURNS[Trajectory]; ArcTo: PROC[t: Trajectory, p1, p2: Pair] RETURNS[Trajectory]; Polygon: TYPE = REF PolygonRep; PolygonRep: TYPE = RECORD[length: NAT, vertices: SEQUENCE maxLength: NAT OF Pair]; Outline: TYPE = REF OutlineRep; OutlineRep: TYPE = RECORD[list: LIST OF Trajectory]; MakeOutline: PROC[LIST OF Trajectory] RETURNS[Outline]; MaskFill: PROC[context: Context, outline: REF]; SetStrokeWidth: PROC[context: Context, strokeWidth: REAL]; SetStrokeEnd: PROC[context: Context, strokeEnd: StrokeEnd]; defaultStrokeWidth: REAL = nullREAL; defaultStrokeEnd: StrokeEnd = nil; MaskStroke: PROC[context: Context, t: Trajectory, strokeWidth: REAL _ defaultStrokeWidth, strokeEnd: StrokeEnd _ defaultStrokeEnd]; MaskStrokeClosed: PROC[context: Context, t: Trajectory, strokeWidth: REAL _ defaultStrokeWidth]; MaskVector: PROC[context: Context, p1, p2: Pair, strokeWidth: REAL _ defaultStrokeWidth, strokeEnd: StrokeEnd _ defaultStrokeEnd]; MaskRectangle: PROC[context: Context, x, y, w, h: REAL]; IntegerMaskRectangle: PROC[context: Context, x, y, w, h: INTEGER]; StartUnderline: PROC[context: Context]; MaskUnderline: PROC[context: Context, dy, h: REAL]; IntegerMaskUnderline: PROC[context: Context, dy, h: INTEGER]; MaskPixel: PROC[context: Context, pa: PixelArray]; ClipOutline: PROC[context: Context, outline: REF]; ExcludeOutline: PROC[context: Context, outline: REF]; ClipRectangle: PROC[context: Context, x, y, w, h: REAL]; ExcludeRectangle: PROC[context: Context, x, y, w, h: REAL]; IntegerClipRectangle: PROC[context: Context, x, y, w, h: INTEGER]; IntegerExcludeRectangle: PROC[context: Context, x, y, w, h: INTEGER]; MakeFont: PROC[name: ROPE, size: REAL] RETURNS[FONT]; SetFont: PROC[context: Context, font: FONT]; ShowChar: PROC[context: Context, char: CHAR, font: FONT _ NIL]; ShowCharacters: PROC[context: Context, characters: RopeOrRefText, font: FONT _ NIL, start: INT _ 0, length: INT _ LAST[INT]]; SetAmplifySpace: PROC[context: Context, amplifySpace: REAL]; CorrectMask: PROC[context: Context]; CorrectSpace: PROC[context: Context, v: Pair]; Correct: PROC[context: Context, body: PROC]; SetCorrectMeasure: PROC[context: Context, v: Pair]; SetCorrectTolerance: PROC[context: Context, v: Pair]; SetCorrectShrink: PROC[context: Context, correctShrink: REAL]; Space: PROC[context: Context, x: REAL]; IntegerSpace: PROC[context: Context, x: INTEGER]; Reset: PROC[context: Context]; IntPair: TYPE = ImagerBasic.IntPair; IntRectangle: TYPE = ImagerBasic.IntRectangle; SetView: PROC[context: Context, box: IntRectangle, halftoneOrigin: IntPair _ [0, 0]]; ClipView: PROC[context: Context, box: IntRectangle, exclude: BOOL]; DrawBitmap: PROC[context: Context, base: LONG POINTER, raster: CARDINAL, area: IntRectangle]; MaskBits: PROC[context: Context, base: LONG POINTER, raster: CARDINAL, tile: IntRectangle, area: IntRectangle]; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair]; XOR: Color; MakeStipple: PROC[CARDINAL] RETURNS[Color]; TestRectangle: PROC[context: Context, x, y, w, h: REAL] RETURNS[Visibility]; GetSurfaceBounds: PROC[context: Context] RETURNS[IntRectangle]; GetViewBounds: PROC[context: Context] RETURNS[IntRectangle]; SpecialOp: PROC[context: Context, op: ATOM, data: REF] RETURNS[REF]; END. @°Imager.mesa Last edited by: Doug Wyatt, October 11, 1983 3:47 pm Michael Plass, October 26, 1983 3:58 pm The imaging model The Imager synthesizes a complex image on a page by repeatedly laying down simple primitive images. For example, a single Imager operation might place an image of a specific character at a specific position on the page. A subsequent operator might place another character somewhere else, and so on until a complex image is built up. A painter performs a very similar set of operations to create a complex image: he selects a brush, dips it in paint, and lays down a stroke of color. Complex images are created by a series of these simple actions. The imaging model involves three objects:  The page image. The page image is a two-dimensional image that accumulates results from primitive images being laid down. It plays the role of the painter's canvas.  The mask. The mask, specified for each primitive image to be added to the page image, determines exactly where the page image will be modified. In effect, the mask specifies an opening through which ink can be pressed onto the page image. The mask thus plays the role of the painter's brush stroke.  The color. The color specifies the color of the ink to be pushed through the mask onto the page image in order to add the primitive image to the page image; it may take on many colors, various shades of gray (including white and solid black), and transparent. To continue the painting analogy, the color specifies the color of paint in which to dip the brush. The Imager makes complicated images, then, by specifying a sequence of (mask, color) pairs to be laid down on the page image. Invocations of mask operators actually cause the page image to be altered. The color to be used is held in a state variable in the imager context; it applies to all masks until the color is changed. At the beginning of each page, a new page image is initialized to ``paper-white,'' or the natural color of the material on which the image is being formed. The imaging operators, then, will control the ink deposited on the material. In other imaging applications, the initial state of the page image is chosen to achieve as nearly as possible the same effect. If images are being made on a television display, the initial state of the entire display is white. If images are being made on film, the initial state of the film is transparent, which would correspond to white if the film were projected with a white light. Although the following discussion frequently refers to "pages" for convenience, the Imager can be used to create images on any two-dimensional medium. Types Errors This may be an enumerated type someday. Creation For making a new context. The data field is optional; its type is dependent on the deviceType. Imager state The state of the imager is contained in two places: (1) the page image itself, which is not directly accessible through the Imager interface, and (2) the state of two sets of variables: the persistent imager variables and the non-persistent imager variables. Operators are provided for reading or writing these variables. Here are all the imager variables, with their types and initial values: Persistent: cpx, cpy: REAL _ 0 correctMX, correctMY: REAL _ 0 Non-persistent: T: Transformation _ identityTransformation priorityImportant: BOOL _ FALSE mediumXSize, mediumYSize: REAL _ 0 fieldXMin, fieldYMin: REAL _ 0 fieldXMax, fieldYMax: REAL _ 0 showVec: Vector _ emptyVector color: Color _ black noImage: BOOL _ FALSE strokeWidth: REAL _ 0 strokeEnd: StrokeEnd _ square underlineStart: REAL _ 0 amplifySpace: REAL _ 1 correctPass: [0..2] _ 0 correctShrink: REAL _ 0.5 correctTX, correctTY: REAL _ 0 clipOutline: Outline _ FullField[] The variables differ in their treatment by the Do-operators below: the non-persistent variables are restored by DoSave; all variables are restored by DoSaveAll. Set the current value of priorityImportant. A change to the image induced by a mask operator is said to be ordered if priorityImportant is TRUE, and unordered if priorityImportant is FALSE. The rule is that the priority order of all ordered image changes must be preserved; the imager is allowed to alter priority order among unordered changes, or between ordered and unordered changes. Because preserving priority order may require more computation than allowing arbitrary reordering of objects, creators should leave priorityImportant FALSE if possible (this is the default). Transformations Equivalent to ConcatT[context, Translate[x, y]] Equivalent to ConcatT[context, Rotate[a]]. The angle a is meaured in degrees. The rotation can be viewed in two ways: it will rotate coordinate axes clockwise by the angle a, while it will rotate geometrical figures counterclockwise by the angle a. Equivalent to ConcatT[context, Scale[s]] Equivalent to ConcatT[context, Scale2[sx, sy]] Modify T so that the origin maps to the current position. Modify T so that the origin maps to the rounded current position. The rounding in Trans implies that any coordinates to which T is subsequently applied will be translated by an integral number of grid points. This convention allows often-used instances such as characters to be scan-converted once and then translated at will. Trans is designed together with SetXYRel (see below) to achieve positioning precision, while still letting each instance of a character be scan-converted identically. Current position operators The imaging operators make it easy to locate a graphical object such as a character at the current position. The current position is measured in the view coordinate system, and is recorded in two persistent imager variables, cpx and cpy. It is by altering the current position that an operator displaying a character specifies where the next character on the text line should usually lie. The following operators change the current position. Set the current position to p. Precisely, [cpx, cpy] _ Transform[T, p] Add the relative displacement v to the current position. Precisely, [cpx, cpy] _ VectorAdd[[cpx, cpy] , TransformVec[T, v]] Add a relative displacement in the x direction to the current position. Equivalent to SetXYRel[context, [x, 0]] Add a relative displacement in the y direction to the current position. Equivalent to SetXYRel[context, [0, y]] Color The color that will be deposited on the page image is determined by the value of the color variable when a mask operator is invoked. Wherever the mask allows it, the color specified by the imager variable color is deposited on the page image, obliterating any color previously laid down at the same position on the page. There are two ways to specify color: a constant color, and color sampled on a raster. A value of type Color fully specifies a color; a subtype ConstantColor is used for constant colors (ConstantColor widens to Color). Make a shade of gray specified by the fraction f: 0 means white, 1 means black. Set the value of the color variable. Mask operators The mask operators are the central focus of the imager, for they determine the shapes of primitive images that are laid down on the image. Mask operators are available to make images of rectangles, line drawings, or filled outlines, and to use a pixel array to specify samples of the mask. When a mask operator is executed, the page image is altered. The operation of a mask operator is controlled in part by its arguments and in part by imager variables:  The current transformation, T, transforms the specified mask shape to determine the coordinates of the mask on the image.  The current color governs the color of the object that will be placed on the image.  If priorityImportant is TRUE, the priority order of objects laid down is preserved.  If noImage is TRUE, mask operators will have no effect on the image, although they will have the proper effect on the imager variables. Shapes are defined geometrically in termes of segments, trajectories, and outlines. A segment is a directed segment of a straight line, cubic curve, or conic section; it has a start point and an end point. A trajectory is a sequence of connected segments; the end point of a segment coincides with the start point ot the next one. A closed trajectory is a trajectory that closes upon itself, that is, the end point of the last segment in the trajectory coincides with the start point of the first segment. An outline is a collection of trajectories; each trajectory in an outline is implicitly closed by a straight-line segment linking the end point of the last segment with the start point of the first segment. Trajectories may be constructed with the following operators. MoveTo creates a new trajectory; LineTo, CurveTo, and ConicTo add a segment and return an extended trajectory. Trajectories are immutable values. A last point (lp) is always associated with a trajectory: it is the end point of the last segment in the trajectory. Return t's lp. Create a new trajectory whose lp is p. Extend t with a straight line segment from t's lp to p. The lp of the result is p. Equivalent to LineTo[t, [x, LastPoint[t].y]]. Equivalent to LineTo[t, [LastPoint[t].x, y]]. Extend t with a cubic curve segment. The segment is a Bezier curve defined by four control points: t's lp, p1, p2, and p3 in order. The lp of the result is p3. Bezier curves are extremely versatile: any parametric cubic curve can be expressed as a Bezier curve; a simple computation converts coefficients of the parametric cubic equations into Bezier control points. A wide range of curves can be generated by fitting several Bezier curves together so as to preserve continuity at the joints. Note that because the x and y components of a Bezier curve are defined by independent parametric equations, the same curve results whether the curve is first drawn and then transformed into another coordinate system, or the control points are first transformed and then used to draw the Bezier curve defined by them. Extend t with a segment of a conic section. Let p0=LastPoint[t], and let M be the midpoint of the segment between p0 and p2. The conic starts at p0, ends at p2, and intersects the segment from M to p1 in a point I. The ratio of the length of segment MI to the length of the segment from M to p1 is equal to r. The conic piece is bounded by the triangle with vertices p0, p1, and p2, and is a straight line if r=0, an ellipse if 0Jšžœœœ˜'šž œœœ˜1J˜—J˜—™šžœœ˜JšœC™CJ™—Jšœ œ˜$šœœ˜.J˜—šžœœH˜UJšœµ™µJ™—šžœœ/œ˜CJ™CJ™—š ž œœœœ œ˜]J™—š žœœœœ œ*˜o™èJ™šœ"™"šœ™J™Jšœ™—J™Jšœ™—Jšœ2™2—J™—šžœœ9˜SJ™J˜—šœ˜ J˜—šž œœœœ˜+J˜—šž œœœœ ˜LJ˜—šžœœœ˜?J™cJ™—šž œœœ˜