NewImagerImpl.mesa
Michael Plass, February 20, 1984 9:15:15 am PST
Doug Wyatt, June 28, 1984 5:05:02 pm PDT
DIRECTORY
Font USING [FONT],
Imager,
ImagerBasic USING [ColorRep, Pair, PixelArray, StrokeEnd, Transformation],
Rope USING [ROPE];
ImagerImpl: CEDAR PROGRAM
EXPORTS Imager
~ BEGIN
Imager contexts
Creation
Context: TYPE ~ REF ContextRep;
ContextRep: TYPE ~ RECORD[class: Class, data: REF, state: REF];
Create: PUBLIC PROC[deviceType: ATOM, data: REFNIL] RETURNS [Context] ~ {
For making a new context.
The data field is optional; its type is dependent on the deviceType.
};
Errors
Error: PUBLIC ERROR[errorCode: ErrorCode] ~ CODE;
State
The state of the imager is contained in two places: the page image, and the imager variables.
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.
DoSave: PUBLIC PROC[context: Context, body: PROC] ~ {
};
DoSaveAll: PUBLIC PROC[context: Context, body: PROC] ~ {
};
Priority
SetPriorityImportant: PUBLIC PROC[context: Context, priorityImportant: BOOL] ~ {
Set the current value of priorityImportant.
context.class.SetPriorityImportant[context, priorityImportant];
};
Property lists
GetProp: PUBLIC PROC[context: Context, key: REF] RETURNS [value: REF] ~ {
RETURN[Atom.GetPropFromList[context.propList, key]];
};
PutProp: PUBLIC PROC[context: Context, key: REF, value: REF] ~ {
context.propList ← Atom.PutPropOnList[context.propList, key, val];
};
RemProp: PUBLIC PROC[context: Context, key: REF] ~ {
context.propList ← Atom.RemPropFromList[context.propList, key];
};
Transformations
Creating transformations
MakeT: PROC[Matrix] RETURNS[Transformation] ~ {
};
OpenT: PROC[Transformation] RETURNS[Matrix] ~ {
};
Scale: PROC[s: REAL] RETURNS[Transformation] ~ {
};
Scale2: PROC[sx, sy: REAL] RETURNS[Transformation] ~ {
};
Rotate: PROC[a: REAL] RETURNS[Transformation] ~ {
};
Translate: PROC[x, y: REAL] RETURNS[Transformation] ~ {
};
Concat: PROC[m, n: Transformation] RETURNS[Transformation] ~ {
};
Invert: PROC[m: Transformation] RETURNS[Transformation] ~ {
};
Applying transformations
Transform: PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
};
TransformVec: PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
};
InverseTransform: PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
};
InverseTransformVec: PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
};
The current transformation
[[pixelsToMeters ??]]
pointsToMeters: Transformation; -- printer's points, 72.27 to the inch
ConcatT: PROC[context: Context, m: Transformation] ~ {
context.class.ConcatT[context, m];
};
ScaleT: PROC[context: Context, s: REAL] ~ {
context.class.ScaleT[context, s];
};
Scale2T: PROC[context: Context, sx, sy: REAL] ~ {
context.class.Scale2T[context, sx, sy];
};
RotateT: PROC[context: Context, a: REAL] ~ {
context.class.RotateT[context, a];
};
TranslateT: PROC[context: Context, x, y: REAL] ~ {
context.class.TranslateT[context, x, y];
};
Move: PROC[context: Context] ~ {
context.class.Move[context];
};
Trans: PROC[context: Context] ~ {
context.class.Trans[context];
};
Mask operators
Trajectories
MoveToP: PROC[p: Pair] RETURNS[Trajectory] ~ {
};
MoveTo: PROC[x, y: REAL] RETURNS[Trajectory]
~ INLINE { RETURN MoveToP[[x, y]] };
Create a new trajectory whose lp is p.
LineToP: PROC[t: Trajectory, p: Pair] RETURNS[Trajectory];
LineTo: PROC[t: Trajectory, x, y: REAL] RETURNS[Trajectory]
~ INLINE { RETURN t.LineToP[[x, y]] };
Extend t with a straight line segment. The lp of the result is p.
LineToX: PROC[t: Trajectory, x: REAL] RETURNS[Trajectory];
LineToY: PROC[t: Trajectory, y: REAL] RETURNS[Trajectory];
LineToX is equivalent to t.LineTo[x, t.LastPoint.y].
LineToY is equivalent to t.LineTo[t.LastPoint.x, y].
CurveToP: PROC[t: Trajectory, p1, p2, p3: Pair] RETURNS[Trajectory];
CurveTo: PROC[t: Trajectory, x1, y1, x2, y2, x3, y3: REAL] RETURNS[Trajectory]
~ INLINE { RETURN t.CurveToP[[x1, y1], [x2, y2], [x3, y3]] };
Extend t with a cubic curve segment. The lp of the result is p3.
Let p0=t.LastPoint. The curve segment is defined by Bezier control points p0, p1, p2, p3. It starts at p0, tangent to the line joining p0 and p1, and ends at p3, tangent to the line joining p2 and p3. It is bounded by the quadrilateral with vertices p0, p1, p2, p3.
ConicToP: PROC[t: Trajectory, p1, p2: Pair, r: REAL] RETURNS[Trajectory];
ConicTo: PROC[t: Trajectory, x1, y1, x2, y2: REAL, r: REAL] RETURNS[Trajectory]
~ INLINE { RETURN t.ConicToP[[x1, y1], [x2, y2], r] };
Extend t with a segment of a conic section. The lp of the result is p2.
Let p0=t.LastPoint. Let m be the midpoint of the line joining p0 and p2. Let p be the point on the line joining m and p1 such that Length[m, p]/Length[m, p1] = r. The curve segment starts at p0, passes through p, and ends at p2. It is a line if r=0; an ellipse if 0<r<1/2; a parabola if r=1/2; a hyperbola if 1/2<r<1. It is bounded by the triangle with vertices p0, p1, p2.
ArcToP: PROC[t: Trajectory, p1, p2: Pair] RETURNS[Trajectory];
ArcTo: PROC[t: Trajectory, x1, y1, x2, y2: REAL] RETURNS[Trajectory]
~ INLINE { RETURN t.ArcToP[[x1, y1], [x2, y2]] };
Extend t with a circular arc. The lp of the result is p2.
Let p0=t.LastPoint. The arc starts at p0, passes through p1, and ends at p2. It is recommended that p1 lie near the halfway point along the arc. If p0 and p2 are coincident, ArcTo produces a circle; p1 lies on the circle diametrically opposite p0. If p0, p1, and p2 are colinear, the effect is t.LineToP[p1].LineToP[p2].a
PathProc: TYPE ~ PROC[
data: REF,
moveTo: PROC[x, y: REAL],
lineTo: PROC[x, y: REAL],
curveTo: PROC[x1, y1, x2, y2, x3, y3: REAL],
conicTo: PROC[x1, y1, x2, y2: REAL, r: REAL],
arcTo: PROC[x1, y1, x2, y2: REAL]
];
MapTrajectory: PathProc ~ {
WITH data SELECT FROM
end: Trajectory => {
moveTo[end.lp];
FOR t: Trajectory ← end, t.prev UNTIL t.prev=NIL DO
p0: Pair ~ t.prev.lp;
WITH t SELECT FROM
t: REF TrajectoryRep[move] => ERROR; -- should have had t.prev=NIL
t: REF TrajectoryRep[line] => lineTo[p0];
t: REF TrajectoryRep[curve] => curveTo[t.p2, t.p1, p0];
t: REF TrajectoryRep[conic] => conicTo[t.p1, p0, x.r];
t: REF TrajectoryRep[arc] => arcTo[t.p1, p0];
ENDCASE => ERROR; -- unknown variant
ENDLOOP;
};
ENDCASE => ERROR; -- data is wrong type or NIL
};
MapTrajectoryList: PathProc ~ {
WITH data SELECT FROM
head: TrajectoryList => {
FOR list: TrajectoryList ← head, list.rest UNTIL list=NIL DO
t: Trajectory ~ list.first;
MapTrajectory[t, moveTo, lineTo, curveTo, conicTo, arcTo];
ENDLOOP;
};
ENDCASE => ERROR; -- data is wrong type or NIL
};
Outlines
MaskFill: PROC[context: Context, outline: REF] ~ {
WITH outline SELECT FROM
path: PATH => context.class.MaskFill[context, MapPath, path];
t: Trajectory => context.class.MaskFill[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.MaskFill[context, MapTrajectoryList, tlist];
ENDCASE => ERROR; -- outline is wrong type or NIL
};
MaskFillPath: PROC[context: Context, pathProc: PathProc, pathData: REFNIL] ~ {
context.class.MaskFill[context, pathProc, pathData];
};
Strokes
SetStrokeWidth: PUBLIC PROC[context: Context, strokeWidth: REAL] ~ {
context.class.SetStrokeWidth[context, strokeWidth];
};
SetStrokeEnd: PUBLIC PROC[context: Context, strokeEnd: StrokeEnd] ~ {
context.class.SetStrokeEnd[context, strokeEnd];
};
MaskStroke: PUBLIC PROC[context: Context, stroke: REF] ~ {
WITH stroke SELECT FROM
path: PATH => context.class.MaskStroke[context, MapPath, path];
t: Trajectory => context.class.MaskStroke[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.MaskStroke[context, MapTrajectoryList, tlist];
ENDCASE => ERROR; -- stroke is wrong type or NIL
};
MaskStrokeClosed: PUBLIC PROC[context: Context, stroke: REF] ~ {
WITH stroke SELECT FROM
path: PATH => context.class.MaskStrokeClosed[context, MapPath, path];
t: Trajectory => context.class.MaskStrokeClosed[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.MaskStrokeClosed[context, MapTrajectoryList, tlist];
ENDCASE => ERROR; -- stroke is wrong type or NIL
};
MaskStrokePath: PUBLIC PROC[
context: Context, pathProc: PathProc, pathData: REFNIL] ~ {
context.class.MaskStroke[context, pathProc, pathData];
};
MaskStrokeClosedPath: PUBLIC PROC[
context: Context, pathProc: PathProc, pathData: REFNIL] ~ {
context.class.MaskStrokeClosed[context, pathProc, pathData];
};
Rectangles and vectors
MaskRectangle: PUBLIC PROC[context: Context, x, y, w, h: REAL] ~ {
context.class.MaskRectangle[context: context, x: x, y: y, w: w, h: h];
};
MaskBox: PUBLIC PROC[context: Context, box: Box] ~ {
context.class.MaskRectangle[context: context,
x: box.xmin, y: box.ymin, w: box.xmax-box.xmin, h: box.ymax-box.ymin];
};
MaskVectorP: PUBLIC PROC[context: Context, p1, p2: Pair] ~ {
context.class.MaskVector[context: context, x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y];
};
MaskVector: PUBLIC PROC[context: Context, x1, y1, x2, y2: REAL] ~ {
context.class.MaskVector[context: context, x1: x1, y1: y1, x2: x2, y2: y2];
};
Text
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.
SetXYP: PROC[context: Context, p: Pair];
SetXY: PROC[context: Context, x, y: REAL]
~ INLINE { context.SetXYP[[x, y]] };
SetXYI: PROC[context: Context, x, y: INTEGER]
~ INLINE { context.class.SetXYI[context, x, y] };
Set the current position.
SetXYRelP: PROC[context: Context, p: Pair];
SetXYRel: PROC[context: Context, x, y: REAL]
~ INLINE { context.SetXYRelP[[x, y]] };
SetXYRelI: PROC[context: Context, x, y: INTEGER]
~ INLINE { context.class.SetXYRelI[context, x, y] };
Add a relative displacement to the current position.
SetXRel: PROC[context: Context, x: REAL];
SetXRelI: PROC[context: Context, x: INTEGER]
~ INLINE { context.class.SetXYRelI[context, x, 0] };
Equivalent to context.SetXYRel[x, 0]
SetYRel: PROC[context: Context, y: REAL];
SetYRelI: PROC[context: Context, y: INTEGER]
~ INLINE { context.class.SetXYRelI[context, 0, y] };
Equivalent to context.SetXYRel[0, y]
Character operators
The most common shapes are those used to make images of characters; these masks are specified in sets of pre-defined operators called fonts.
FONT: TYPE ~ Font.FONT;
ROPE: TYPE ~ Rope.ROPE;
RopeOrRefText: TYPE ~ REF; -- ROPE or REF TEXT
FindFont: PROC[name: ROPE] RETURNS[FONT];
ModifyFont: PROC[font: FONT, m: Transformation] RETURNS[FONT];
MakeFont: PROC[name: ROPE, size: REAL] RETURNS[FONT];
SetFont: PROC[context: Context, font: FONT];
ShowChar: PROC[context: Context, char: CHAR, font: FONTNIL];
ShowCharacters: PROC[context: Context, characters: RopeOrRefText,
start: INT ← 0, length: INTINT.LAST, font: FONTNIL];
SetAmplifySpace: PROC[context: Context, amplifySpace: REAL];
Underlining
Character strings can be underlined by placing a rectangle of appropriate width and height just below the string. The width of the rectangle will be determined by the width of the character string. The position of the underline along the baseline will be determined by the current position, but because of spacing corrections the current position cannot be anticipated accurately when the master is created. The operators StartUnderline and MaskUnderline are provided to help position underlines accurately. They assume a master coordinate system in which the baseline is oriented in the positive x direction.
StartUnderline: PROC[context: Context];
Remember the x component of the current position as the starting point for an underline.
MaskUnderline: PROC[context: Context, dy, h: REAL];
MaskUnderlineI: PROC[context: Context, dy, h: INTEGER]
~ INLINE { context.class.MaskUnderlineI[context, dy, h] };
The text starting at the point previously identified by StartUnderline and ending at the current position will be underlined with a rectangle of height h and top a distance dy below the current position. For example, to underline the word Hello, the client might call:
StartUnderline[context]; ShowCharacters[context, "Hello"]; MaskUnderline[context, 4, 1];
Spacing correction
CorrectMask: PROC[context: Context];
CorrectSpaceP: PROC[context: Context, p: Pair];
CorrectSpace: PROC[context: Context, x, y: REAL]
~ INLINE { context.CorrectSpaceP[[x, y]] };
Correct: PROC[context: Context, body: PROC];
SetCorrectMeasureP: PROC[context: Context, p: Pair];
SetCorrectMeasure: PROC[context: Context, x, y: REAL]
~ INLINE { context.SetCorrectMeasureP[[x, y]] };
SetCorrectToleranceP: PROC[context: Context, p: Pair];
SetCorrectTolerance: PROC[context: Context, x, y: REAL]
~ INLINE { context.SetCorrectToleranceP[[x, y]] };
SetCorrectShrink: PROC[context: Context, correctShrink: REAL];
Space: PROC[context: Context, x: REAL];
SpaceI: PROC[context: Context, x: INTEGER]
~ INLINE { context.class.SpaceI[context, x] };
Pixel arrays
PixelArray: TYPE ~ ImagerBasic.PixelArray;
MakePixelArrayFromBits: PROC[
bitPointer: LONG POINTER TO PACKED ARRAY [0..0) OF [0..1],
bitsPerLine, samplesPerLine, numberOfLines: NAT
] RETURNS [PixelArray];
Manufactures a one-bit-per-sample/one-sample-per-pixel PixelArray from a bitmap. The bitmap is assumed to be oriented like a screen bitmap, so
bitPointer^[0] is the sample for the upper-left-hand corner,
bitPointer^[samplesPerLine-1] is for the upper-right-hand corner, and
bitPointer^[bitsPerLine*(numberOfLines-1)+samplesPerLine-1] is for the lower-right-hand corner.
MaskPixel: PROC[context: Context, pa: PixelArray];
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).
Constant color
ConstantColor: TYPE ~ REF ImagerBasic.ColorRep[constant];
MakeGray: PROC[f: REAL] RETURNS[ConstantColor];
Make a shade of gray specified by the fraction f: 0 means white, 1 means black.
black: ConstantColor; -- MakeGray[1]
white: ConstantColor; -- MakeGray[0]
Sampled color
Color: TYPE ~ REF ImagerBasic.ColorRep;
SetSampledColor: PROC[context: Context, pa: PixelArray, pixelT: Transformation, colorOperator: ATOM ← $Intensity];
SetSampledBlack: PROC[context: Context, pa: PixelArray, pixelT: Transformation, transparent: BOOLEANFALSE];
These operations set a sampled color with a transformation of
Concat[pixelT, context.state.T].
For example, if the current coordinate system were in meters, and the client wished to set a 10-by-10 stipple pattern that repeated every 1 cm, the following would work:
pattern: PACKED ARRAY [0..100) OF [0..1] ← [1, 0, 1, 1, ... 0];
pixelArray: PixelArray;
TRUSTED {pixelArray ← MakePixelArrayFromBits[@pattern, 10, 10, 10]};
SetSampledBlack[context, pixelArray, Scale[0.001]];
The current color
SetColor: PROC[context: Context, color: Color];
Set the value of the color variable.
SetGray: PROC[context: Context, f: REAL];
Equivalent to context.SetColor[MakeGray[f]]
Clipping
ClipOutline: PROC[context: Context, outline: REF];
ClipOutlinePath: PROC[context: Context, pathProc: PathProc, pathData: REFNIL];
ExcludeOutline: PROC[context: Context, outline: REF];
ExcludeOutlinePath: PROC[context: Context, pathProc: PathProc, pathData: REFNIL];
ClipRectangle: PROC[context: Context, x, y, w, h: REAL];
ClipRectangleI: PROC[context: Context, x, y, w, h: INTEGER];
ExcludeRectangle: PROC[context: Context, x, y, w, h: REAL];
ExcludeRectangleI: PROC[context: Context, x, y, w, h: INTEGER];
Private details
Class: TYPE ~ REF ClassRep;
ClassRep: TYPE ~ RECORD[
deviceType: ATOM,
Init: PROC [context: Context, info: REF] ←,
DoSave: PROC[context: Context, body: PROC] ←,
DoSaveAll: PROC[context: Context, body: PROC] ←,
SetPriorityImportant: PROC[context: Context, priorityImportant: BOOL] ←,
ConcatT: PROC[context: Context, m: Transformation] ←,
ScaleT: PROC[context: Context, s: REAL] ←,
Scale2T: PROC[context: Context, sx, sy: REAL] ←,
RotateT: PROC[context: Context, a: REAL] ←,
TranslateT: PROC[context: Context, x, y: REAL] ←,
Move: PROC[context: Context] ←,
Trans: PROC[context: Context] ←,
SetXY: PROC[context: Context, x, y: REAL] ←,
SetXYI: PROC[context: Context, x, y: INTEGER] ←,
SetXYRel: PROC[context: Context, x, y: REAL] ←,
SetXYRelI: PROC[context: Context, x, y: INTEGER] ←,
MaskFill: PROC[context: Context, pathProc: PathProc, pathData: REF] ←,
MaskStroke: PROC[context: Context, pathProc: PathProc, pathData: REF] ←,
MaskStrokeClosed: PROC[context: Context, pathProc: PathProc, pathData: REF] ←,
MaskVector: PROC[context: Context, x1, y1, x2, y2: REAL] ←,
MaskVectorI: PROC[context: Context, x1, y1, x2, y2: INTEGER] ←,
MaskRectangle: PROC[context: Context, x, y, w, h: REAL] ←,
MaskRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ←,
StartUnderline: PROC[context: Context] ←,
MaskUnderline: PROC[context: Context, dy, h: REAL] ←,
MaskUnderlineI: PROC[context: Context, dy, h: INTEGER] ←,
MaskPixel: PROC[context: Context, pa: PixelArray] ←,
SetGray: PROC[context: Context, f: REAL] ←,
SetColor: PROC[context: Context, color: Color] ←,
SetSampledColor: PROC[context: Context, pa: PixelArray, pixelT: Transformation, colorOperator: ATOM] ←,
SetSampledBlack: PROC[context: Context, pa: PixelArray, pixelT: Transformation, transparent: BOOL] ←,
ClipOutline: PROC[context: Context, pathProc: PathProc, pathData: REF] ←,
ExcludeOutline: PROC[context: Context, pathProc: PathProc, pathData: REF] ←,
ClipRectangle: PROC[context: Context, x, y, w, h: REAL] ←,
ClipRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ←,
ExcludeRectangle: PROC[context: Context, x, y, w, h: REAL] ←,
ExcludeRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ←,
SetFont: PROC[context: Context, font: FONT] ←,
ShowChar: PROC[context: Context, char: CHAR] ←,
ShowCharacters: PROC[context: Context, characters: REF, start: INT, length: INT] ←,
CorrectMask: PROC[context: Context] ←,
CorrectSpace: PROC[context: Context, x, y: REAL] ←,
SetCorrectMeasure: PROC[context: Context, x, y: REAL] ←,
SetCorrectTolerance: PROC[context: Context, x, y: REAL] ←,
SetCorrectShrink: PROC[context: Context, correctShrink: REAL] ←,
SetAmplifySpace: PROC[context: Context, amplifySpace: REAL] ←,
Space: PROC[context: Context, x: REAL] ←,
SpaceI: PROC[context: Context, x: INTEGER] ←,
Correct: PROC[context: Context, body: PROC] ←
];
END.