ImagerImpl.mesa
Michael Plass, February 20, 1984 9:15:15 am PST
Doug Wyatt, July 3, 1984 3:25:43 pm PDT
DIRECTORY
Atom USING [DottedPair, DottedPairNode, PropList],
Basics USING [bitsPerWord],
Font USING [CreateScaled],
Imager USING [Box, Color, ConstantColor, Context, ErrorCode, FONT, Matrix, Pair, PathProc, StrokeEnd, Trajectory, TrajectoryList, TrajectoryRep, Transformation],
ImagerBasic USING [ColorRep, PixelArray],
ImagerConic USING [FromArc],
ImagerMasks USING [PixelArrayFromPixelMap],
ImagerPixelMaps USING [Create, PixelMap],
ImagerTransform USING [Concat, Contents, FromRec, InverseTransform, InverseTransformVec, Invert, Rotate, Scale, Scale2, Transform, TransformVec, Translate],
PrincOps USING [BBTableSpace, BBptr],
PrincOpsUtils USING [AlignedBBTable, BITBLT],
Real USING [RoundLI],
Rope USING [ROPE];
ImagerImpl: CEDAR PROGRAM
IMPORTS Font, ImagerConic, ImagerMasks, ImagerPixelMaps, ImagerTransform, PrincOpsUtils, Real
EXPORTS Imager
~ BEGIN OPEN Imager;
ROPE: TYPE ~ Rope.ROPE;
Imager contexts
Creation
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.
ERROR;
};
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] ~ {
context.class.DoSave[context, body];
};
DoSaveAll: PUBLIC PROC[context: Context, body: PROC] ~ {
context.class.DoSaveAll[context, body];
};
Priority
SetPriorityImportant: PUBLIC PROC[context: Context, priorityImportant: BOOL] ~ {
context.class.SetPriorityImportant[context, priorityImportant];
};
Property lists
PropList: TYPE ~ Atom.PropList;
DottedPair: TYPE ~ Atom.DottedPair;
DottedPairNode: TYPE ~ Atom.DottedPairNode;
The Imager's property list mechanism regards a PropList as an immutable value. We don't use the procedures from Atom since 1) they might smash an existing PropList, and 2) they hold a global monitor in AtomImpl.
RemPropFromList: PROC[p: PropList, key: REF] RETURNS[PropList] ~ {
IF p=NIL THEN RETURN[NIL]
ELSE IF p.first.key=key THEN RETURN[p.rest]
ELSE {
rest: PropList ~ RemPropFromList[p.rest, key];
IF rest=p.rest THEN RETURN[p]
ELSE RETURN[CONS[p.first, rest]];
};
};
PutPropOnList: PROC[p: PropList, key: REF, value: REF] RETURNS[PropList] ~ {
rest: PropList ~ RemPropFromList[p, key];
IF value=NIL THEN RETURN[rest]
ELSE {
pair: DottedPair ~ NEW[DottedPairNode ← [key, value]];
RETURN[CONS[pair, rest]];
};
};
GetPropFromList: PROC[p: PropList, key: REF] RETURNS[value: REF] ~ {
FOR list: PropList ← p, list.rest UNTIL list=NIL DO
pair: DottedPair ~ list.first;
IF pair.key=key THEN RETURN[pair.val];
ENDLOOP;
RETURN[NIL];
};
GetProp: PUBLIC PROC[context: Context, key: REF] RETURNS[value: REF] ~ {
RETURN[GetPropFromList[context.propList, key]];
};
PutProp: PUBLIC PROC[context: Context, key: REF, value: REF] ~ {
context.propList ← PutPropOnList[context.propList, key, value];
};
RemProp: PUBLIC PROC[context: Context, key: REF] ~ {
context.propList ← RemPropFromList[context.propList, key];
};
Transformations
Creating transformations
MakeT: PUBLIC PROC[m: Matrix] RETURNS[Transformation] ~ {
RETURN[ImagerTransform.FromRec[m]]
};
OpenT: PUBLIC PROC[m: Transformation] RETURNS[Matrix] ~ {
RETURN[ImagerTransform.Contents[m]];
};
Scale: PUBLIC PROC[s: REAL] RETURNS[Transformation] ~ {
RETURN[ImagerTransform.Scale[s]];
};
Scale2: PUBLIC PROC[sx, sy: REAL] RETURNS[Transformation] ~ {
RETURN[ImagerTransform.Scale2[sx, sy]];
};
Rotate: PUBLIC PROC[a: REAL] RETURNS[Transformation] ~ {
RETURN[ImagerTransform.Rotate[a]];
};
Translate: PUBLIC PROC[x, y: REAL] RETURNS[Transformation] ~ {
RETURN[ImagerTransform.Translate[x, y]];
};
Concat: PUBLIC PROC[m, n: Transformation] RETURNS[Transformation] ~ {
RETURN[ImagerTransform.Concat[m, n]];
};
Invert: PUBLIC PROC[m: Transformation] RETURNS[Transformation] ~ {
RETURN [ImagerTransform.Invert[m]]
};
Applying transformations
Transform: PUBLIC PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
RETURN[ImagerTransform.Transform[p, m]]
};
TransformVec: PUBLIC PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
RETURN[ImagerTransform.TransformVec[p, m]]
};
InverseTransform: PUBLIC PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
RETURN[ImagerTransform.InverseTransform[p, m]]
};
InverseTransformVec: PUBLIC PROC[m: Transformation, p: Pair] RETURNS[Pair] ~ {
RETURN[ImagerTransform.InverseTransformVec[p, m]]
};
The current transformation
ConcatT: PUBLIC PROC[context: Context, m: Transformation] ~ {
context.class.ConcatT[context, m];
};
ScaleT: PUBLIC PROC[context: Context, s: REAL] ~ {
context.class.ScaleT[context, s];
};
Scale2T: PUBLIC PROC[context: Context, sx, sy: REAL] ~ {
context.class.Scale2T[context, sx, sy];
};
RotateT: PUBLIC PROC[context: Context, a: REAL] ~ {
context.class.RotateT[context, a];
};
TranslateT: PUBLIC PROC[context: Context, x, y: REAL] ~ {
context.class.TranslateT[context, x, y];
};
Move: PUBLIC PROC[context: Context] ~ {
context.class.Move[context];
};
Trans: PUBLIC PROC[context: Context] ~ {
context.class.Trans[context];
};
Mask operators
Trajectories
LastPoint: PUBLIC PROC[t: Trajectory] RETURNS[x, y: REAL] ~ {
RETURN[t.lp.x, t.lp.y];
};
LastPointP: PUBLIC PROC[t: Trajectory] RETURNS[Pair] ~ {
RETURN[t.lp];
};
MoveTo: PUBLIC PROC[x, y: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[move] ← [prev: NIL, lp: [x, y], variant: move[]]]];
};
MoveToP: PUBLIC PROC[p: Pair] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[move] ← [prev: NIL, lp: p, variant: move[]]]];
};
LineTo: PUBLIC PROC[t: Trajectory, x, y: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[line] ← [prev: t, lp: [x, y], variant: line[]]]];
};
LineToP: PUBLIC PROC[t: Trajectory, p: Pair] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[line] ← [prev: t, lp: p, variant: line[]]]];
};
LineToX: PUBLIC PROC[t: Trajectory, x: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[line] ← [prev: t, lp: [x, t.lp.y], variant: line[]]]];
};
LineToY: PUBLIC PROC[t: Trajectory, y: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[line] ← [prev: t, lp: [t.lp.x, y], variant: line[]]]];
};
CurveTo: PUBLIC PROC[t: Trajectory, x1, y1, x2, y2, x3, y3: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[curve] ← [prev: t, lp: [x3, y3], variant: curve[[x1, y1], [x2, y2]]]]];
};
CurveToP: PUBLIC PROC[t: Trajectory, p1, p2, p3: Pair] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[curve] ← [prev: t, lp: p3, variant: curve[p1, p2]]]];
};
ConicTo: PUBLIC PROC[t: Trajectory, x1, y1, x2, y2: REAL, r: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[conic] ← [prev: t, lp: [x2, y2], variant: conic[[x1, y1], r]]]];
};
ConicToP: PUBLIC PROC[t: Trajectory, p1, p2: Pair, r: REAL] RETURNS[Trajectory] ~ {
RETURN[NEW[TrajectoryRep[conic] ← [prev: t, lp: p2, variant: conic[p1, r]]]];
};
ArcTo: PUBLIC PROC[t: Trajectory, x1, y1, x2, y2: REAL] RETURNS[Trajectory] ~ {
conic: PROC[p1, p2: Pair, r: REAL] ~ {t ← ConicToP[t, p1, p2, r]};
ImagerConic.FromArc[t.lp, [x1, y1], [x2, y2], conic];
RETURN[t];
};
ArcToP: PUBLIC PROC[t: Trajectory, p1, p2: Pair] RETURNS[Trajectory] ~ {
conic: PROC[p1, p2: Pair, r: REAL] ~ {t ← ConicToP[t, p1, p2, r]};
ImagerConic.FromArc[t.lp, p1, p2, conic];
RETURN[t];
};
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: PUBLIC PathProc ~ {
WITH data SELECT FROM
end: Trajectory => {
move[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] => line[p0];
t: REF TrajectoryRep[curve] => curve[t.p2, t.p1, p0];
t: REF TrajectoryRep[conic] => conic[t.p1, p0, t.r];
t: REF TrajectoryRep[arc] => arc[t.p1, p0];
ENDCASE => ERROR; -- unknown variant
ENDLOOP;
};
ENDCASE => ERROR; -- data is wrong type or NIL
};
MapTrajectoryList: PUBLIC PathProc ~ {
WITH data SELECT FROM
head: TrajectoryList => {
FOR list: TrajectoryList ← head, list.rest UNTIL list=NIL DO
t: Trajectory ~ list.first;
MapTrajectory[t, move, line, curve, conic];
ENDLOOP;
};
ENDCASE => ERROR; -- data is wrong type or NIL
};
Outlines
MaskFill: PUBLIC PROC[context: Context, outline: REF] ~ {
WITH outline SELECT FROM
t: Trajectory => context.class.MaskFill[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.MaskFill[context, MapTrajectoryList, tlist];
path: PATH => context.class.MaskFill[context, MapPath, path];
ENDCASE => ERROR Error[$InvalidOutline]; -- outline is wrong type or NIL
};
MaskFillPath: PUBLIC 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
t: Trajectory => context.class.MaskStroke[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.MaskStroke[context, MapTrajectoryList, tlist];
path: PATH => context.class.MaskStroke[context, MapPath, path];
ENDCASE => ERROR Error[$InvalidStroke]; -- stroke is wrong type or NIL
};
MaskStrokeClosed: PUBLIC PROC[context: Context, stroke: REF] ~ {
WITH stroke SELECT FROM
t: Trajectory => context.class.MaskStrokeClosed[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.MaskStrokeClosed[context, MapTrajectoryList, tlist];
path: PATH => context.class.MaskStrokeClosed[context, MapPath, path];
ENDCASE => ERROR Error[$InvalidStroke]; -- 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];
};
MaskVector: PUBLIC PROC[context: Context, x1, y1, x2, y2: REAL] ~ {
context.class.MaskVector[context: context, x1: x1, y1: y1, x2: x2, y2: y2];
};
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];
};
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.
SetXY: PUBLIC PROC[context: Context, x, y: REAL] ~ {
context.class.SetXY[context, x, y];
};
SetXYP: PUBLIC PROC[context: Context, p: Pair] ~ {
context.class.SetXY[context, p.x, p.y];
};
SetXYRel: PUBLIC PROC[context: Context, x, y: REAL] ~ {
context.class.SetXYRel[context, x, y];
};
SetXYRelP: PUBLIC PROC[context: Context, p: Pair] ~ {
context.class.SetXYRel[context, p.x, p.y];
};
SetXRel: PUBLIC PROC[context: Context, x: REAL] ~ {
context.class.SetXYRel[context, x, 0];
};
SetYRel: PUBLIC PROC[context: Context, y: REAL] ~ {
context.class.SetXYRel[context, 0, y];
};
Character operators
FindFont: PUBLIC PROC[name: ROPE] RETURNS[FONT] ~ {
RETURN[NIL];
};
ModifyFont: PUBLIC PROC[font: FONT, m: Transformation] RETURNS[FONT] ~ {
RETURN[NIL];
};
MakeFont: PUBLIC PROC[name: ROPE, size: REAL] RETURNS[FONT] ~ {
RETURN[Font.CreateScaled[name, size]];
};
SetFont: PUBLIC PROC[context: Context, font: FONT] ~ {
context.class.SetFont[context, font];
};
ShowRope: PUBLIC PROC[context: Context,
rope: ROPE, start: INT ← 0, len: INTINT.LAST] ~ {
context.class.ShowRope[context, rope, start, len];
};
ShowText: PUBLIC PROC[context: Context,
text: REF READONLY TEXT, start: NAT ← 0, len: NATNAT.LAST] ~ {
context.class.ShowText[context, text, start, len];
};
ShowChar: PUBLIC PROC[context: Context, char: CHAR] ~ {
context.class.ShowChar[context, char];
};
SetAmplifySpace: PUBLIC PROC[context: Context, amplifySpace: REAL] ~ {
context.class.SetAmplifySpace[context, amplifySpace];
};
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: PUBLIC PROC[context: Context] ~ {
context.class.StartUnderline[context];
};
MaskUnderline: PUBLIC PROC[context: Context, dy, h: REAL] ~ {
context.class.MaskUnderline[context, dy, h];
};
Spacing correction
CorrectMask: PUBLIC PROC[context: Context] ~ {
context.class.CorrectMask[context];
};
CorrectSpace: PUBLIC PROC[context: Context, x, y: REAL] ~ {
context.class.CorrectSpace[context, x, y];
};
CorrectSpaceP: PUBLIC PROC[context: Context, p: Pair] ~ {
context.class.CorrectSpace[context, p.x, p.y];
};
Correct: PUBLIC PROC[context: Context, body: PROC] ~ {
context.class.Correct[context, body];
};
SetCorrectMeasure: PUBLIC PROC[context: Context, x, y: REAL] ~ {
context.class.SetCorrectMeasure[context, x, y];
};
SetCorrectMeasureP: PUBLIC PROC[context: Context, p: Pair] ~ {
context.class.SetCorrectMeasure[context, p.x, p.y];
};
SetCorrectTolerance: PUBLIC PROC[context: Context, x, y: REAL] ~ {
context.class.SetCorrectTolerance[context, x, y];
};
SetCorrectToleranceP: PUBLIC PROC[context: Context, p: Pair] ~ {
context.class.SetCorrectTolerance[context, p.x, p.y];
};
SetCorrectShrink: PUBLIC PROC[context: Context, correctShrink: REAL] ~ {
context.class.SetCorrectShrink[context, correctShrink];
};
Space: PUBLIC PROC[context: Context, x: REAL] ~ {
context.class.Space[context, x];
};
Pixel arrays
PixelArray: TYPE ~ ImagerBasic.PixelArray;
MakePixelArrayFromBits: PUBLIC PROC[
bitPointer: LONG POINTER TO PACKED ARRAY [0..0) OF [0..1],
bitsPerLine, samplesPerLine, numberOfLines: NAT]
RETURNS [pixelArray: PixelArray] ~ TRUSTED {
pixelMap: ImagerPixelMaps.PixelMap ← ImagerPixelMaps.Create[0, [0, 0, numberOfLines, samplesPerLine]];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
bb^ ← [
dst: [word: pixelMap.refRep.pointer, bit: 0],
dstBpl: pixelMap.refRep.rast*Basics.bitsPerWord,
src: [word: bitPointer, bit: 0],
srcDesc: [srcBpl[bitsPerLine]],
height: numberOfLines,
width: samplesPerLine,
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]
];
PrincOpsUtils.BITBLT[bb];
pixelArray ← ImagerMasks.PixelArrayFromPixelMap[pixelMap];
pixelArray.m ← pixelArray.m.Concat[ImagerTransform.Rotate[-90]].Concat[ImagerTransform.Translate[0, numberOfLines]];
};
MaskPixel: PUBLIC PROC[context: Context, pa: PixelArray] ~ {
context.class.MaskPixel[context, pa];
};
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
Card: PROC[real: REAL] RETURNS[card: CARDINAL] ~ {
int: INT ← Real.RoundLI[real*LAST[CARDINAL]];
card ← MAX[MIN[int, LAST[CARDINAL]], 0];
};
MakeGray: PUBLIC PROC[f: REAL] RETURNS[ConstantColor] ~ {
IF f<0 OR f>1 THEN ERROR Error[$GrayParameterOutOfRange];
RETURN[NEW[ImagerBasic.ColorRep[constant] ←
[constant[x: Card[0.3101], y: Card[0.3163], Y: Card[1-f]]]]];
};
black: PUBLIC ConstantColor ← MakeGray[1];
white: PUBLIC ConstantColor ← MakeGray[0];
Sampled color
Color: TYPE ~ REF ImagerBasic.ColorRep;
SetSampledColor: PUBLIC PROC[context: Context,
pa: PixelArray, pixelT: Transformation, colorOperator: ATOM ← $Intensity] ~ {
context.class.SetSampledColor[context, pa, pixelT, colorOperator];
};
SetSampledBlack: PUBLIC PROC[context: Context,
pa: PixelArray, pixelT: Transformation, transparent: BOOLFALSE] ~ {
context.class.SetSampledBlack[context, pa, pixelT, transparent];
};
The current color
SetColor: PUBLIC PROC[context: Context, color: Color] ~ {
context.class.SetColor[context, color];
};
SetGray: PUBLIC PROC[context: Context, f: REAL] ~ {
context.class.SetGray[context, f];
};
Clipping
ClipOutline: PUBLIC PROC[context: Context, outline: REF] ~ {
WITH outline SELECT FROM
t: Trajectory => context.class.ClipOutline[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.ClipOutline[context, MapTrajectoryList, tlist];
path: PATH => context.class.MaskFill[context, MapPath, path];
ENDCASE => ERROR Error[$InvalidOutline]; -- outline is wrong type or NIL
};
ClipOutlinePath: PUBLIC PROC[context: Context,
pathProc: PathProc, pathData: REFNIL] ~ {
context.class.ClipOutline[context, pathProc, pathData];
};
ExcludeOutline: PUBLIC PROC[context: Context, outline: REF] ~ {
WITH outline SELECT FROM
t: Trajectory => context.class.ExcludeOutline[context, MapTrajectory, t];
tlist: TrajectoryList => context.class.ExcludeOutline[context, MapTrajectoryList, tlist];
path: PATH => context.class.MaskFill[context, MapPath, path];
ENDCASE => ERROR Error[$InvalidOutline]; -- outline is wrong type or NIL
};
ExcludeOutlinePath: PUBLIC PROC[context: Context,
pathProc: PathProc, pathData: REFNIL] ~ {
context.class.ExcludeOutline[context, pathProc, pathData];
};
ClipRectangle: PUBLIC PROC[context: Context, x, y, w, h: REAL] ~ {
context.class.ClipRectangle[context, x, y, w, h];
};
ExcludeRectangle: PUBLIC PROC[context: Context, x, y, w, h: REAL] ~ {
context.class.ExcludeRectangle[context, x, y, w, h];
};
END.