JunoGraphicsImpl.mesa
Coded July 1982 by Donna M. Auguste & Greg Nelson
Edited December 7, 1982 5:10 pm
Last Edited by: Gnelson, June 27, 1983 3:51 pm
Last Edited by: Stolfi, June 7, 1984 2:12:47 pm PDT

These are procedures which directly affect the Juno bitmap.

Imported by JunoTop and its submodules (JunoAlgebraImpl, JunoBody, JunoOldSolver...)

DIRECTORY

JunoGraphics,
Font USING [WidthVector],
JunoUserEvents USING[],
Real USING [RoundI],
Rope USING [ROPE, Cat, Map, ActionType, Equal],
Graphics USING [Context, Translate, Scale, SetCP, GetBounds,
SetColor, black, SetStipple, DrawBox],
GraphicsOps USING [BitmapRef, NewBitmap, DrawBitmap, NewContextFromBitmap],
Carets USING [StartCaret, StopCaret],
Imager USING [MakeFont, FONT, Trajectory, Context, XOR, black, white, MoveTo, LineTo,
LineToX, LineToY, Create, SpecialOp, Pair, SetXYRel, SetXY, Trans, ClipRectangle,
ScaleT, SetPriorityImportant, SetColor, MaskRectangle, MakeGray, SetStrokeEnd,
SetStrokeWidth, SetFont, DoSave, MaskFill, MaskStroke, MaskVector, CurveTo,
ShowCharacters, ShowChar, Reset],
ImagerPixelMaps USING [PixelMap, CreateFrameBuffer, Clear],
ImagerBasic USING [ColorRep],
ImagerPD USING [PDFileDescription, Raven, Puffin, PlateMaker];

JunoGraphicsImpl: CEDAR MONITOR

IMPORTS

Font,
Real,
Graphics,
GraphicsOps,
Carets,
Imager,
ImagerPD,
ImagerPixelMaps,
Rope

EXPORTS

JunoGraphics =

BEGIN

OPEN

JunoGraphics,
Evs: JunoUserEvents,
Im: Imager,
ImMaps: ImagerPixelMaps,
ImPD: ImagerPD,
ImBas: ImagerBasic,
Rope;

- - - - GRAPHICS STATE

The variables below need not be protected by the monitor lock, since they are set and used only by the main process (JunoTop).

buf: REF ImMaps.PixelMap ← NIL; -- REF to Imager-style descriptor of buffer bitmap

bufCtx: Im.Context; -- A context that paints into buf. Origin at bottom left, 1 dot/unit

ctx: Im.Context ← NIL; -- Either bufCtx or a PD context (or NIL at first).

invert: PUBLIC Color ← Im.XOR;
black: PUBLIC Color ← Im.black;
white: PUBLIC Color ← Im.white;

currentColor: PUBLIC Color ← Im.black;
currentEnds: PUBLIC ATOM ← $round;
currentWidth: PUBLIC REAL ← 0;
currentFontName: PUBLIC ROPE ← "Helvetica";
currentFontSize: PUBLIC REAL ← 12;
currentFontFace: PUBLIC ATOM ← $regular;
currentJustification: PUBLIC ATOM ← $left;

currentFont: Im.FONT;

- - - - CHANGING THE GRAPHIC STATE

SetScreenContext: PUBLIC PROC =

BEGIN

IF ctx # NIL AND ctx # bufCtx THEN
{[] ← Im.SpecialOp[ctx, $Close, NIL]};

ctx ← bufCtx;
ImMaps.Clear[buf^];
ResetCtx[width: 0, color: invert];
PicChanged[]

END;

SetPaperContext: PUBLIC PROC [device: ATOM, fileName: ROPE, mag: REAL ← 1.0] =

BEGIN

pageSize: Coords = SELECT device FROM
$Raven, $Puffin => [8.5, 11],
$PlateMaker => [8.5, 11], -- check these numbers
ENDCASE => ERROR;

paperCtx: Im.Context = Im.Create[$PD, SELECT device FROM
$Raven => ImPD.Raven[fileName],
$Puffin => ImPD.Puffin[fileName],
$PlateMaker => ImPD.PlateMaker[fileName],
ENDCASE => ERROR];

IF ctx # bufCtx AND ctx # NIL THEN
{[] ← Im.SpecialOp[ctx, $Close, NIL]};

ctx ←paperCtx;
ResetCtx[width: 0.4, color: black, mag: mag];
Im.SetXY[ctx, [pageSize.x*72/2/mag, pageSize.y*72/2/mag]];
Im.SetXYRel[ctx, [-currentPictureWidth/2, -currentPictureHeight/2]];
Im.Trans[ctx];
Im.ClipRectangle[ctx, 0, 0, currentPictureWidth, currentPictureHeight]

END;

ResetCtx: PROC [width: REAL, color: Color, mag: REAL ← 1.0] =

BEGIN

Im.Reset[ctx];
Im.ScaleT[ctx, 0.0254/72/mag];

Im.MaskRectangle [bufCtx, 300, 300, 150, 20]; --DEBUG
Im.MaskRectangle [bufCtx, 300, -300, 150, 20]; --DEBUG

Im.SetPriorityImportant[ctx, TRUE];
DoSetFont[ctx: ctx, name: "Helvetica", face: $regular, size: 12];
[] ← SetEnds[$round];
[] ← SetColor[color];
[] ← SetWidth[width];
[] ← SetJustification[$left]

END;

SetColor: PUBLIC PROC [color: Color] RETURNS [old: Color] =

BEGIN

old ← currentColor;
Im.SetColor[ctx, color];
currentColor ← color

END;

IntensityToColor: PUBLIC PROC [intensity: REAL] RETURNS [color: Color] =

BEGIN

color ← Im.MakeGray[intensity]

END;

RGBToColor: PUBLIC PROC [r, g, b: REAL] RETURNS [color: Color] =

BEGIN

color ← NEW [ImBas.ColorRep[constant] ←
[constant
[x: Real.RoundI[r*10000],
y: Real.RoundI[g*10000],
Y: Real.RoundI[b*10000]]]]

END;

SetEnds: PUBLIC PROC [ends: ATOM] RETURNS [old: ATOM] =

BEGIN

old ← currentEnds;
Im.SetStrokeEnd
[ctx,
SELECT ends FROM
$round => round,
$square => square,
$butt => butt,
ENDCASE => ERROR];
currentEnds ← ends

END;

SetWidth: PUBLIC PROC [width: REAL] RETURNS [old: REAL] =

BEGIN

old ← currentWidth;
Im.SetStrokeWidth [ctx, width];
currentWidth ← width

END;

SetFontName: PUBLIC PROC [name: ROPE] RETURNS [old: ROPE] =

BEGIN

old ← currentFontName;
IF NOT Equal[currentFontName, name] THEN
{DoSetFont [ctx, name, currentFontFace, currentFontSize]}

END;

SetFontSize: PUBLIC ENTRY PROC [size: REAL] RETURNS [old: REAL] =

BEGIN

old ← currentFontSize;
IF currentFontSize # size THEN
{DoSetFont [ctx, currentFontName, currentFontFace, size]}

END;

SetFontFace: PUBLIC ENTRY PROC [face: ATOM] RETURNS [old: ATOM] =

BEGIN

old ← currentFontFace;
IF currentFontFace # face THEN
{DoSetFont [ctx, currentFontName, face, currentFontSize]}

END;

DoSetFont: PROC [ctx: Im.Context, name: ROPE, face: ATOM, size: REAL] =

BEGIN

currentFont ← Im.MakeFont
[name: Cat["Xerox/PressFonts/", name,
SELECT face FROM
$regular => "/MRR",
$italic => "/MIR",
$bold => "/BRR",
$boldItalic => "/BIR",
ENDCASE => ERROR],
size: size];

currentFontName ← name;
currentFontFace ← face;
currentFontSize ← size;

Im.SetFont [ctx, currentFont]

END;

SetJustification: PUBLIC PROC [justification: ATOM] RETURNS [old: ATOM] =

BEGIN

old ← currentJustification;
currentJustification ← justification

END;

- - - - IMAGE BLANKING

Whiten: PUBLIC PROC =

BEGIN

IF ctx = bufCtx THEN
{ImMaps.Clear[buf^]}
ELSE
{Im.SetColor[ctx, white];
Im.MaskRectangle[ctx, -1, -1, currentPictureWidth+1, currentPictureHeight+1];
Im.SetColor[ctx, currentColor]};

PicChanged[]

END;

- - - - POINT HIGHLIGHTS (AFFECTS SCREEN IMAGE ONLY)

Trajectories for point highlights (Shouldn't we use a special symbol font instead?):

plusTraj: Im.Trajectory = Im.MoveTo[[-1, -1]].LineTo[[-5, -1]].LineTo[[-5, +1]]
.LineTo[[-1, +1]].LineTo[[-1, +5]].LineTo[[+1, +5]].LineTo[[+1, +1]]
.LineTo[[+5, +1]].LineTo[[+5, -1]].LineTo[[+1, -1]].LineTo[[+1, -5]]
.LineTo[[-1,-5]];

crossTraj: Im.Trajectory = Im.MoveTo[[-1, 0]].LineTo[[-4, +3]].LineTo[[-3, +4]]
.LineTo[[0, +1]].LineTo[[+3, +4]].LineTo[[+4, +3]].LineTo[[+1, 0]]
.LineTo[[+4, -3]].LineTo[[+3, -4]].LineTo[[0, -1]].LineTo[[-3, -4]]
.LineTo[[-4,-3]];

diam5Traj: Im.Trajectory = Im.MoveTo[[-5,0]].LineTo[[0,-5]].LineTo[[5, 0]].LineTo[[0,5]];

diam4Traj: Im.Trajectory = Im.MoveTo[[-4,0]].LineTo[[0,-4]].LineTo[[4, 0]].LineTo[[0,4]];

box4Traj: Im.Trajectory = Im.MoveTo[[-4,-4]].LineToX[+4].LineToY[+4].LineToX[-4];

box3Traj: Im.Trajectory = Im.MoveTo[[-3,-3]].LineToX[+3].LineToY[+3].LineToX[-3];

SetOrg: PROC [ctx: Im.Context, coords: Coords] = INLINE
Redefines origin at the given coordinates, rounded to the nearest pixel

{Im.SetXY[ctx, coords]; Im.Trans[ctx]};

Hilyte: PUBLIC PROC [coords: Coords, symbol: PointSymbol] =

BEGIN

DoShow: PROC =

BEGIN

Im.SetColor[ctx, invert];

SetOrg[ctx, coords];

SELECT symbol FROM

box =>

{Im.MaskRectangle[ctx, -4, -4, 8, 8];
Im.MaskRectangle[ctx, -3, -3, 6, 6]};

plus =>

{Im.MaskFill[ctx, plusTraj]};

cross =>

{Im.MaskFill[ctx, crossTraj]};

diamond =>

{Im.MaskFill[ctx, diam5Traj];
Im.MaskFill[ctx, diam4Traj]};

dot =>

{Im.MaskRectangle[ctx, -1, -1, 2, 2]};

bigDot =>

{Im.MaskRectangle[ctx, -2, -2, 4, 4]};

ENDCASE => ERROR;

END;

IF ctx # bufCtx THEN RETURN;

Im.DoSave[ctx, DoShow];
PicChanged[]

END;

- - - - POINTS

Dummy trajectory to draw a point:

dotTraj: Im.Trajectory = Im.MoveTo[[0, 0]].LineTo[[0, 0]];

DrawPoint: PUBLIC PROC[coords: Coords] =

BEGIN

DoDraw: PROC =

BEGIN

Im.SetStrokeEnd[ctx, $round];

SetOrg[ctx, coords];

Im.MaskStroke[ctx, dotTraj];

END;

Im.DoSave[ctx, DoDraw];
PicChanged[]

END;

- - - - LINES, STROKES, FILLED PATHS

DrawEdge: PUBLIC PROC [p, q: Coords, thin: BOOLTRUE] =

BEGIN

Im.MaskVector [context: ctx, p1: [p.x, p.y], p2: [q.x, q.y],
strokeWidth: IF thin THEN 0 ELSE currentWidth];
PicChanged[]

END;

DrawArc: PUBLIC PROC [p, r, s, q: Coords, thin: BOOLTRUE] =

BEGIN

Im.MaskStroke [context: ctx, t: Im.MoveTo[p].CurveTo[r, s, q],
strokeWidth: IF thin THEN 0 ELSE currentWidth];
PicChanged[]

END;

AppendEdge: PUBLIC PROC [t: Trajectory, p, q: Coords] RETURNS [new: Trajectory] =

BEGIN

IF t = NIL THEN t ← Im.MoveTo[p];
new ← t.LineTo[q];

END;

AppendArc: PUBLIC PROC [t: Trajectory, p, r, s, q: Coords] RETURNS [new: Trajectory] =

BEGIN

IF t = NIL THEN t ← Im.MoveTo[p];
new ← t.CurveTo[r, s, q];

END;

FillTrajectory: PUBLIC PROC [t: Trajectory] =

BEGIN

Im.MaskFill[ctx, t];
PicChanged[]

END;

StrokeTrajectory: PUBLIC PROC [t: Trajectory] =

BEGIN

Im.MaskStroke[ctx, t];
PicChanged[]

END;

GcTrajectory: PUBLIC PROC [t: Trajectory] =

BEGIN

END;

- - - - CHARS AND ROPES

DrawChar: PUBLIC PROC [coords: Coords, char: CHAR] =

BEGIN

DoDraw: PROC = {SetOrg[ctx, coords]; Im.ShowChar[ctx, char]};
Im.DoSave[ctx, DoDraw];
PicChanged[]

END;

DrawRope: PUBLIC PROC [coords: Coords, rope: ROPE] =

BEGIN

LeftEndPoint: PROC RETURNS [lep: Coords] = INLINE

{IF currentJustification = $left THEN
{RETURN [coords]}
ELSE
{vec: Coords ← GetRopeDispl[rope];
IF currentJustification = $right THEN
{RETURN [[coords.x-vec.x, coords.y-vec.y]]}
ELSE
{RETURN [[coords.x-vec.x/2, coords.y-vec.y/2]]}}};

DoDraw: PROC = {SetOrg[ctx, LeftEndPoint[]]; Im.ShowCharacters[ctx, rope]};

Im.DoSave[ctx, DoDraw];
PicChanged[]

END;

GetRopeDispl: PUBLIC PROC[rope: ROPE] RETURNS [vec: Coords] =

BEGIN

Act: ActionType = TRUSTED
{w: Coords = Font.WidthVector[currentFont, c];
vec ← [vec.x+w.x, vec.y+w.y]};

vec ← [0,0];
[] ← Map[base: rope, action: Act]

END;

- - - - BITMAP BUFFER

The following data (except picChanged and the bits in the bitmap buffer) must be protected by the monitor lock, since they are altered by the main client process (JunoTop) and consulted by the bitmap-to-viewer dumping process (RefreshViewer).

currentPictureWidth: PUBLIC INTEGER ← 0; -- current picture dimensions (Juno units)
currentPictureHeight: PUBLIC INTEGER ← 0;

gbuf: GraphicsOps.BitmapRef ← NIL; -- REF to Graphics-style descriptor of buffer bitmap

bufferPos: IntCoords ← [0, 0]; -- position of origin of bitmap buffer wrt origin of viewer

picChanged: PUBLIC BOOLEANTRUE;
Signals that something (bits, buffer size, buffer offset, caret position) changed since last painting on viewer. Set by operations that paint on buf. Reset by PaintBuffer after dumping buf onto viewer.

bufferChanged: BOOLEANTRUE;
Set by operations that change buffer size or position, reset by PaintProc after dumping buf onto viewer.

PicChanged: ENTRY PROC = INLINE {picChanged ← TRUE};

- - - - BUFFER-RELATED PROCEDURES

CreateBitmap: PROC [width, height: INTEGER]
RETURNS [im: REF ImMaps.PixelMap, gra: GraphicsOps.BitmapRef] =
This abominable crock creates a new bitmap of the specified size, and two descriptors to it, one suitable to paint into with the Imager, the other suitable to paint from with GraphisOps.

BEGIN

gra ← GraphicsOps.NewBitmap [width, height];
TRUSTED{im ← NEW [ImMaps.PixelMap ← ImMaps.CreateFrameBuffer
[pointer: LOOPHOLE [gra.base],
words: gra.raster*gra.height,
lgBitsPerPixel: 0,
rast: gra.raster,
lines: gra.height]]}

END;

SetPictureSize: PUBLIC ENTRY PROC [width, height: INTEGER] =

BEGIN

IF buf=NIL OR width # currentPictureWidth OR height # currentPictureHeight THEN
{tmpCtx: Im.Context;
[buf, gbuf] ← CreateBitmap[width, height];
tmpCtx ← Im.Create[deviceType: $LFDisplay, data: buf];
IF ctx # NIL AND ctx = bufCtx THEN ctx ← tmpCtx;
bufCtx ← tmpCtx;
currentPictureWidth ← width; currentPictureHeight ← height};

ImMaps.Clear[buf^];
bufferChanged ← picChanged ← TRUE

END;

- - - - BLINKING CARET

The following data is also protected by the monitor lock:

caretReallyOn: BOOLFALSE; -- if TRUE, caret is currently on (set by PaintBuffer)

caretOn: PUBLIC BOOLFALSE; -- if TRUE, caret is being turned on (set by SetCaret)

caretPos: PUBLIC Coords; -- current caret position, in Juno coordinates

SetCaret: PUBLIC ENTRY PROC [on: BOOLTRUE, coords: Coords←[0,0]] =

{caretOn ← on; caretPos ← coords;
picChanged ← TRUE};

- - - - VIEWER/JUNO COORDINATE MAPPING

ViewerToJuno: PUBLIC ENTRY PROC
[viewer: Viewer, view: IntCoords] RETURNS [coords: IntCoords] =

{coords.x ← view.x - bufferPos.x;
coords.y ← (viewer.ch - view.y) - bufferPos.y};

InternalJunoToViewer: INTERNAL PROC
[viewer: Viewer, coords: Coords] RETURNS [view: IntCoords] = INLINE

{view.x ← Real.RoundI[coords.x]+bufferPos.x;
view.y ← viewer.ch - (Real.RoundI[coords.y] + bufferPos.y)};

JunoToViewer: PUBLIC ENTRY PROC
[viewer: Viewer, coords: Coords] RETURNS [view: IntCoords] =

{RETURN [InternalJunoToViewer[viewer, coords]]};

- - - - BUFFER PAINTING

PaintBuffer: PUBLIC ENTRY PROC
[viewer: Viewer, context: Graphics.Context, viewerChanged: BOOL] =
Paints the bitmap buffer on the viewer.
Also recomputes the Juno-to-Viewer coordinate transformations, to account for the current size of bitmap and viewer.

BEGIN


IF viewerChanged THEN
{bc: Graphics.Context = GraphicsOps.NewContextFromBitmap[gbuf];
Graphics.DrawBox[self: bc, box: [200, 200, 220, 400]];
Graphics.SetStipple[self: context, pattern: 055132B];
Graphics.DrawBox[self: context, box: Graphics.GetBounds[self: context]]};

Graphics.SetColor[self: context, color: Graphics.black];

-- change coordinate system from top-to-bottom to bottom-to-top;
Graphics.Translate[self: context, tx: 0, ty: viewer.ch];
Graphics.Scale[self: context, sx: 1, sy: -1];

Graphics.SetCP[self: context, x: bufferPos.x, y: bufferPos.y];
GraphicsOps.DrawBitmap
[self: context, bitmap: gbuf, w: gbuf.width, h: gbuf.height,
xorigin: 0, yorigin: currentPictureHeight];

IF caretReallyOn AND (NOT caretOn OR viewerChanged OR bufferChanged) THEN
{Carets.StopCaret[primary]; caretReallyOn ← FALSE};

IF caretOn AND NOT caretReallyOn THEN
{view: IntCoords = InternalJunoToViewer[viewer, caretPos];
Carets.StartCaret[viewer, view.x, view.y, primary];
caretReallyOn ← TRUE};
picChanged ← bufferChanged ← FALSE

END;

- - - - INITIALIZATION

SetPictureSize[width: 612, height: 792]; -- 8.5 by 11 inches
SetScreenContext[]

END.