ImagerLFImpl.mesa
Michael Plass, August 4, 1983 11:55 am
DIRECTORY BitBlt, Environment, Imager, ImagerBasic, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerRegistration, ImagerStroke, ImagerTransform, Inline, Real, Rope, RopeInline, Scaled, ScanConverter, TerminalMultiplex, UnifiedFonts, UserTerminal;
ImagerLFImpl: CEDAR MONITOR
IMPORTS BitBlt, Imager, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerRegistration, ImagerStroke, ImagerTransform, Inline, Real, Rope, RopeInline, Scaled, ScanConverter, TerminalMultiplex, UserTerminal
SHARES UnifiedFonts
~ BEGIN
stats: RECORD [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT ← 0];
Stats: PROC RETURNS [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT] = {
loadRunGroups ← stats.loadRunGroups; stats.loadRunGroups ← 0;
loadRasters ← stats.loadRasters; stats.loadRasters ← 0;
rasterChars ← stats.rasterChars; stats.rasterChars ← 0;
runGroupChars ← stats.runGroupChars; stats.runGroupChars ← 0;
clippedChars ← stats.clippedChars; stats.clippedChars ← 0;
culledChars ← stats.culledChars; stats.culledChars ← 0;
};
ROPE: TYPE ~ Rope.ROPE;
FONT: TYPE ~ UnifiedFonts.FONT;
Mask: TYPE ~ ImagerMasks.Mask;
Rectangle: TYPE ~ Imager.Rectangle;
IntRectangle: TYPE ~ Imager.IntRectangle;
Pair: TYPE ~ Imager.Pair;
IntPair: TYPE ~ Imager.IntPair;
Context: TYPE ~ Imager.Context;
Transformation: TYPE ~ Imager.Transformation;
Clipper: TYPE ~ Imager.Clipper;
Visibility: TYPE ~ Imager.Visibility;
Path: TYPE ~ Imager.Path;
StrokeEnds: TYPE ~ Imager.StrokeEnds;
Color: TYPE ~ Imager.Color;
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD [
currentColor: Color,
clientClipper: Mask, -- in device coords
clientClipperPresent: BOOLEAN,
clientClipperExclude: BOOLEAN,
compositeClipperValid: BOOLEAN,
doSaveCount: [0..1000) ← 0,
compositeClipper: Mask, -- in device coords
viewClipper: Mask, -- in device coords
sOriginView, fOriginView: INTEGER,
sOriginBrick, fOriginBrick: INTEGER,
currentPosition: ImagerBasic.Pair,
in device space
transformation: Transformation,
viewToDevice: Transformation,
surfaceToDevice: Transformation,
devicePath: ScanConverter.DevicePath,
lineBuffer: Mask,
tile: Mask,
tileSamples: ImagerBasic.SampledColor,
phase: INTEGER,
canvas: Mask
];
LFBrick: ImagerHalftone.DeviceBrick ← ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255];
Init: PROC [context: Context, name: ROPE, bounds: IntRectangle] ~ TRUSTED {
data: Data ~ NEW[DataRep];
context.data ← data;
data.tile ← ImagerMasks.NewBitmap[0, 0, 16, 16];
data.tileSamples ← MakeTileSamples[16, 16];
data.phase ← 0;
data.devicePath ← ScanConverter.Allocate[];
data.canvas ← ImagerMasks.LFDisplay[];
data.transformation ← data.viewToDevice ← data.surfaceToDevice ← ImagerTransform.Concat[[1,0,0,0,1,0,hard], ImagerTransform.Create[0, -1, data.canvas.SSize, 1, 0, 0]];
data.clientClipperPresent ← data.compositeClipperValid ← FALSE;
data.currentPosition ← [0, 0];
data.viewClipper ← ImagerMasks.Box[sMin: 0, fMin: 0, sSize: data.canvas.SSize, fSize: data.canvas.FSize];
[[data.sOriginView, data.fOriginView]] ← [[data.sOriginBrick, data.fOriginBrick]] ← ImagerTransform.IntTransform[[0,0], data.surfaceToDevice];
SetColor[context, Imager.black];
IF bounds # Imager.nullBounds THEN {
sMin, fMin, sSize, fSize: INTEGER;
[[sMin, fMin, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[bounds, data.transformation];
data.canvas ← ImagerMasks.NewBitmap[sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize];
};
};
Reset: PROC [context: Context] ~ TRUSTED {
data: Data ← NARROW[context.data];
context.data ← data;
data.phase ← 0;
data.devicePath ← ScanConverter.Allocate[];
data.canvas ← ImagerMasks.LFDisplay[];
data.transformation ← data.viewToDevice ← data.surfaceToDevice ← ImagerTransform.Concat[[1,0,0,0,1,0,hard], ImagerTransform.Create[0, -1, data.canvas.SSize, 1, 0, 0]];
data.clientClipperPresent ← data.compositeClipperValid ← FALSE;
data.currentPosition ← [0, 0];
data.viewClipper ← ImagerMasks.Box[sMin: 0, fMin: 0, sSize: data.canvas.SSize, fSize: data.canvas.FSize];
[[data.sOriginView, data.fOriginView]] ← [[data.sOriginBrick, data.fOriginBrick]] ← ImagerTransform.IntTransform[[0,0], data.surfaceToDevice];
SetColor[context, Imager.black];
};
MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: ImagerBasic.SampledColor] ~ {
tileSamples ← NEW[ImagerBasic.SampledColorRep];
tileSamples.transparent ← FALSE;
tileSamples.pa ← NEW[ImagerBasic.PixelArrayRep];
tileSamples.m ← ImagerTransform.Rotate[0];
tileSamples.colorMap ← $Intensity8bpp;
tileSamples.pa.xPixels ← xSize;
tileSamples.pa.yPixels ← ySize;
tileSamples.pa.maxSampleValue ← 255;
tileSamples.pa.samplesPerPixel ← 1;
tileSamples.pa.get ← ConstantGet;
tileSamples.pa.data ← NEW[NAT ← 0];
};
SetTileSamples: PROC [tileSamples: ImagerBasic.SampledColor, intensity: [0..255]] ~ {
WITH tileSamples.pa.data SELECT FROM
n: REF NAT => n^ ← intensity;
ENDCASE => ERROR;
};
ConstantGet: PROC [self: ImagerBasic.PixelArray, buffer: ImagerBasic.PixelBuffer, nSamples: NAT, layer: INT, xStart, yStart: Scaled.Value, xDelta, yDelta: Scaled.Value] ~ {
WITH self.data SELECT FROM
n: REF NAT => {
value: NAT ← n^;
FOR i: NAT IN [0..nSamples) DO buffer[i] ← value ENDLOOP;
};
ENDCASE => ERROR;
};
DoSaveAll: PROC [context: Context, action: PROC] ~ {
data: Data ← NARROW[context.data];
cp: Pair ← data.currentPosition;
DoSave[context, action];
data.currentPosition ← cp;
};
DoSave: PROC [context: Context, action: PROC] ~ {
data: Data ← NARROW[context.data];
color: Color ← data.currentColor;
clipper: Clipper ← GetClipper[context];
transformation: Transformation ← data.transformation;
sOriginView: INTEGER ← data.sOriginView;
fOriginView: INTEGER ← data.fOriginView;
sOriginBrick: INTEGER ← data.sOriginBrick;
fOriginBrick: INTEGER ← data.fOriginBrick;
data.doSaveCount ← data.doSaveCount + 1;
action[];
data.doSaveCount ← data.doSaveCount - 1;
data.sOriginView ← sOriginView;
data.fOriginView ← fOriginView;
data.sOriginBrick ← sOriginBrick;
data.fOriginBrick ← fOriginBrick;
data.transformation ← transformation;
context.SetClipper[clipper];
IF data.currentColor # color THEN SetColor[context, color];
};
Flush: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
};
Close: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
};
TranslateT: PROC [context: Context, dx, dy: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreTranslate[dx, dy, data.transformation];
};
RotateT: PROC [context: Context, degrees: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreRotate[degrees, data.transformation];
};
ScaleT: PROC [context: Context, sx, sy: REAL] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.PreScale[sx, sy, data.transformation];
};
ConcatT: PROC [context: Context, transformation: Transformation] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.Concat[transformation, data.transformation];
};
IntTranslateT: PROC [context: Context, dx, dy: INTEGER] ~ {TranslateT[context, dx, dy]};
IntScaleT: PROC [context: Context, sx, sy: INTEGER] ~ {
data: Data ← NARROW[context.data];
IF sx=1 AND sy=-1 THEN {
data.transformation ← ImagerTransform.Concat[[1, 0, 0, 0, -1, 0, mirrorY], data.transformation]
}
ELSE data.transformation ← ImagerTransform.PreScale[sx, sy, data.transformation];
};
ClipperRep: TYPE ~ RECORD [
exclude: BOOLEAN,
mask: Mask
];
GetClipper: PROC [context: Context] RETURNS [Clipper] ~ {
data: Data ← NARROW[context.data];
IF data.clientClipperPresent THEN {
clipper: REF ClipperRep ← NEW[ClipperRep];
clipper.exclude ← data.clientClipperExclude;
clipper.mask ← data.clientClipper.InlineShift[-data.sOriginView, -data.fOriginView];
ccCount ← ccCount + 1;
RETURN [clipper]
}
ELSE RETURN [NIL]
};
SetClipper: PROC [context: Context, clipper: Clipper] ~ {
data: Data ← NARROW[context.data];
IF clipper = NIL THEN {
data.clientClipperPresent ← FALSE;
data.clientClipper.refRep ← NIL;
ccCount ← ccCount + 1;
}
ELSE {
clipperRef: REF ClipperRep ← NARROW[clipper];
data.clientClipperPresent ← TRUE;
data.clientClipperExclude ← clipperRef.exclude;
data.clientClipper ← clipperRef.mask.Shift[data.sOriginView, data.fOriginView];
ccCount ← ccCount + 1;
};
data.compositeClipperValid ← FALSE;
};
GetT: PROC [context: Context] RETURNS [Transformation] ~ {
data: Data ← NARROW[context.data];
RETURN [ImagerTransform.Concat[data.transformation, ImagerTransform.Invert[data.viewToDevice]]]
};
SetT: PROC [context: Context, transformation: Transformation] ~ {
data: Data ← NARROW[context.data];
data.transformation ← ImagerTransform.Concat[transformation, data.viewToDevice];
};
MaskFromPath: PROC [data: Data, outline: Path] RETURNS [mask: Mask] ~ {
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
m: ImagerBasic.Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
m.d * p.x + m.e * p.y + m.f,
m.a * p.x + m.b * p.y + m.c
]]};
Xmove: PROC [p: Pair] ~ {move[Xform[p]]};
Xline: PROC [p: Pair] ~ {line[Xform[p]]};
Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]};
outline.generateProc[outline, Xmove, Xline, Xcurve];
};
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
data.devicePath.ScanConvert[proc: BoxFromScanConverter];
};
data.devicePath.Reset;
data.devicePath.PushPath[GenPath];
mask ← ImagerMasks.Create[Runs];
data.devicePath.Reset;
};
MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ {
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
m: ImagerBasic.Transformation ← data.transformation;
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (f, s) — note f comes first for ScanConvert!
m.d * p.x + m.e * p.y + m.f,
m.a * p.x + m.b * p.y + m.c
]]};
move[Xform[[area.x, area.y]]];
line[Xform[[area.x + area.w, area.y]]];
line[Xform[[area.x + area.w, area.y + area.h]]];
line[Xform[[area.x, area.y + area.h]]];
};
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
data.devicePath.ScanConvert[proc: BoxFromScanConverter];
};
IF data.transformation.type # hard THEN {
p1: Pair ← ImagerTransform.Transform[[area.x, area.y], data.transformation];
p2: Pair ← ImagerTransform.Transform[[area.x+area.w, area.y+area.h], data.transformation];
sMin: INTEGER ← Real.RoundLI[MAX[MIN[p1.x, p2.x], -LAST[INTEGER]/2]];
sMax: INTEGER ← Real.RoundLI[MIN[MAX[p1.x, p2.x], LAST[INTEGER]/2]];
fMin: INTEGER ← Real.RoundLI[MAX[MIN[p1.y, p2.y], -LAST[INTEGER]/2]];
fMax: INTEGER ← Real.RoundLI[MIN[MAX[p1.y, p2.y], LAST[INTEGER]/2]];
RETURN [ImagerMasks.InlineBox[sMin: sMin, fMin: fMin, sSize: sMax-sMin, fSize: fMax-fMin]];
};
data.devicePath.Reset;
data.devicePath.PushPath[GenPath];
mask ← ImagerMasks.Create[Runs];
data.devicePath.Reset;
};
ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED {
Because of an apparent bug in Real.FScale.
i: INT ← Real.RoundLI[real * 65536.0];
RETURN[LOOPHOLE[i]]
};
MaskFromPixelArray: PROC [data: Data, pixelArray: ImagerBasic.PixelArray] RETURNS [Mask] ~ {
destArea: Mask ← MaskFromRectangle[data, [0, 0, pixelArray.xPixels, pixelArray.yPixels]];
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
buffer: ImagerBasic.PixelBuffer ← NEW[ImagerBasic.PixelBufferRep[destArea.FSize+1]];
DoRun: PROC [s, fMin: INTEGER, fSize: CARDINAL] ~ {
nextPixel: ImagerBasic.Pair ← ImagerTransform.InverseTransformVec[[0, 1], data.transformation];
start: ImagerBasic.Pair ← ImagerTransform.InverseTransform[[0.5+s, 0.5+fMin], data.transformation];
xStart: Scaled.Value ← ScaledFromReal[start.x];
yStart: Scaled.Value ← ScaledFromReal[start.y];
xDeltaPixel: Scaled.Value ← ScaledFromReal[nextPixel.x];
yDeltaPixel: Scaled.Value ← ScaledFromReal[nextPixel.y];
runStart: NAT ← 0;
fRel: NAT ← 0;
pixelArray.get[pixelArray, buffer, fSize, 0, xStart, yStart, xDeltaPixel, yDeltaPixel];
WHILE fRel < fSize DO
buffer[fSize] ← 0;
WHILE buffer[fRel] # 0 DO fRel ← fRel + 1 ENDLOOP;
IF fRel > runStart THEN {run[s, fMin + fRel, fRel - runStart]};
buffer[fSize] ← 1;
WHILE buffer[fRel] = 0 DO fRel ← fRel + 1 ENDLOOP;
runStart ← fRel;
ENDLOOP;
};
destArea.MapRuns[DoRun];
};
RETURN [ImagerMasks.Create[Runs]];
};
CompositeClipper: PROC [data: Data] RETURNS [Mask] ~ {
IF NOT data.compositeClipperValid THEN {
IF data.clientClipperPresent THEN {
IF data.clientClipperExclude THEN data.compositeClipper ← data.viewClipper.Difference[data.clientClipper]
ELSE data.compositeClipper ← data.viewClipper.And[data.clientClipper];
}
ELSE data.compositeClipper ← data.viewClipper;
data.compositeClipperValid ← TRUE;
};
RETURN [data.compositeClipper]
};
ccCount: INT ← 0;
ClipMask: PROC [data: Data, mask: Mask, exclude: BOOLEAN] ~ {
data.compositeClipperValid ← FALSE;
IF NOT data.clientClipperPresent THEN {
data.clientClipperPresent ← TRUE;
data.clientClipperExclude ← exclude;
data.clientClipper ← mask;
ccCount ← ccCount + 1;
}
ELSE IF exclude THEN {
IF data.clientClipperExclude THEN {
data.clientClipper ← data.clientClipper.Or[mask];
ccCount ← ccCount + 1;
}
ELSE {
data.clientClipper ← data.clientClipper.Difference[mask];
ccCount ← ccCount + 1;
};
}
ELSE {
IF data.clientClipperExclude THEN {
data.clientClipper ← mask.Difference[data.clientClipper];
ccCount ← ccCount + 1;
data.clientClipperExclude ← FALSE;
}
ELSE {
data.clientClipper ← data.clientClipper.And[mask];
ccCount ← ccCount + 1;
};
};
};
ClipPath: PROC [context: Context, outline: Path, exclude: BOOLEANFALSE] ~ {
data: Data ← NARROW[context.data];
ClipMask[data, MaskFromPath[data, outline], exclude];
};
ClipRectangle: PROC [context: Context, outline: Rectangle, exclude: BOOLEANFALSE] ~ {
data: Data ← NARROW[context.data];
ClipMask[data, MaskFromRectangle[data, outline], exclude];
};
TestRectangle: PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, area];
RETURN [mask.IsVisible[CompositeClipper[data]]];
};
ClipIntRectangle: PROC [context: Context, outline: IntRectangle, exclude: BOOLEANFALSE] ~ {
ClipRectangle[context, [outline.x, outline.y, outline.w, outline.h], exclude];
};
TestIntRectangle: PROC [context: Context, area: IntRectangle] RETURNS [Visibility] ~ {
RETURN [TestRectangle[context, [area.x, area.y, area.w, area.h]]];
};
DoWithoutClipping: PROC [context: Context, bounds: IntRectangle, callBack: PROC[Context]] ~ {
callBack[context];
};
GetColor: PROC [context: Context] RETURNS [color: Color] ~ {
data: Data ← NARROW[context.data];
color ← data.currentColor;
};
SetColor: PROC [context: Context, color: Color] ~ {
data: Data ← NARROW[context.data];
IF color = data.currentColor THEN RETURN;
data.currentColor ← color;
IF color = Imager.black OR color = Imager.white THEN RETURN;
WITH color SELECT FROM
stipple: REF CARDINAL => TRUSTED {
maskRep: REF ImagerMasks.MaskRep.bitmap ← NARROW[data.tile.refRep];
stippleRows: PACKED ARRAY [0..4) OF [0..16) ← LOOPHOLE[stipple^];
stippleRow: [0..16);
dest: LONG POINTER TO ARRAY [0..16) OF PACKED ARRAY [0..4) OF [0..16) ← maskRep.pointer;
IF maskRep.rast # 1 OR maskRep.lines # 16 THEN ERROR;
stippleRow ← stippleRows[0];
dest[0] ← dest[4] ← dest[8] ← dest[12] ← [stippleRow, stippleRow, stippleRow, stippleRow];
stippleRow ← stippleRows[1];
dest[1] ← dest[5] ← dest[9] ← dest[13] ← [stippleRow, stippleRow, stippleRow, stippleRow];
stippleRow ← stippleRows[2];
dest[2] ← dest[6] ← dest[10] ← dest[14] ← [stippleRow, stippleRow, stippleRow, stippleRow];
stippleRow ← stippleRows[3];
dest[3] ← dest[7] ← dest[11] ← dest[15] ← [stippleRow, stippleRow, stippleRow, stippleRow];
};
constantColor: ImagerBasic.CIEColor => {
intensity: INT ← constantColor.Y;
intensity ← (intensity + 127)/(LAST[CARDINAL]/255);
SetTileSamples[data.tileSamples, intensity];
ImagerHalftone.Halftone[
dest: data.tile,
mask: ImagerMasks.Box[0, 0, 16, 16],
source: data.tileSamples.pa,
transformation: ImagerTransform.Rotate[0],
deviceBrick: LFBrick
];
};
sampledColor: ImagerBasic.SampledColor => NULL;
special: ATOM => {
SELECT special FROM
$XOR => NULL;
ENDCASE => data.currentColor ← Imager.black;
};
ENDCASE => data.currentColor ← Imager.black;
};
MaskStroke: PROC [context: Context, path: Path, width: REAL, strokeEnds: StrokeEnds, closed: BOOLEAN] ~ {
data: Data ← NARROW[context.data];
clipper: ImagerMasks.Mask ← CompositeClipper[data];
mask: ImagerMasks.Mask ← ImagerStroke.MaskFromStroke[path, data.transformation, width, strokeEnds, closed, clipper.sMin, clipper.sMin+clipper.sSize];
ApplyMask[data, mask];
};
ApplyMask: PROC [data: Data, mask: Mask] ~ {
clippedMask: Mask ← mask.And[CompositeClipper[data]];
IF clippedMask.sSize = 0 THEN RETURN;
SELECT data.currentColor FROM
Imager.black => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase];
Imager.white => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase, [and, complement]];
$XOR => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase, [xor, null]];
ENDCASE =>
WITH data.currentColor SELECT FROM
constantColor: ImagerBasic.CIEColor => {
data.canvas.MaskConstant[clippedMask, data.tile, data.phase];
};
stipple: REF CARDINAL => {
data.canvas.MaskConstant[clippedMask, data.tile, data.phase];
};
sampledColor: ImagerBasic.SampledColor => {
transform: Transformation ← ImagerTransform.Concat[sampledColor.m, data.viewToDevice];
ImagerHalftone.Halftone[data.canvas, clippedMask, sampledColor.pa, transform, LFBrick];
};
ENDCASE => ERROR;
};
MaskFill: PROC [context: Context, path: Path] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromPath[data, path];
ApplyMask[data, mask];
};
MaskPixel: PROC [context: Context, pixelArray: ImagerBasic.PixelArray] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromPixelArray[data, pixelArray];
ApplyMask[data, mask];
};
scratchPixelArray: ImagerBasic.PixelArray ← NEW[ImagerBasic.PixelArrayRep];
scratchBitmapDesc: REF ImagerMasks.MaskRep.bitmap ← NEW[ImagerMasks.MaskRep.bitmap];
MaskBits: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, tile: IntRectangle, area: IntRectangle] ~ {
ENABLE UNWIND => NULL;
data: Data ← NARROW[context.data];
color: Color ← data.currentColor;
IF data.transformation.type = rot90 AND tile.w = 16 AND raster = 1 AND tile.h <= 16 AND (color = Imager.black OR color = $XOR) THEN {
sMin, fMin, sSize, fSize: INTEGER;
sMinTile, fMinTile, sSizeTile, fSizeTile: INTEGER;
[[sMin, fMin, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[area, data.transformation];
[[sMinTile, fMinTile, sSizeTile, fSizeTile]] ← ImagerTransform.TransformIntRectangle[tile, data.transformation];
scratchBitmapDesc.pointer ← base;
scratchBitmapDesc.rast ← raster;
scratchBitmapDesc.lines ← sSizeTile;
ImagerMasks.MaskConstant[data.canvas, CompositeClipper[data].And[[sMin, fMin, sSize, fSize, NIL]], [sMinTile, fMinTile, sSizeTile, fSizeTile, scratchBitmapDesc], 0, IF color = Imager.black THEN [or, null] ELSE [xor, null]];
}
ELSE {
IF firstBadMaskBits THEN {firstBadMaskBits ← FALSE; ERROR}
-- sorry, not yet.
};
};
firstBadMaskBits: BOOLEANTRUE;
MaskSegment: PROC [context: Context, p: Pair] ~ {
Not implemented yet
};
MaskIntSegment: PROC [context: Context, p: IntPair] ~ {
Not implemented yet
};
MaskThinStroke: PROC [context: Context, path: Path] ~ {
Not implemented yet
};
MaskRectangle: PROC [context: Context, area: Rectangle] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, area];
ApplyMask[data, mask];
};
MaskIntRectangle: PROC [context: Context, area: IntRectangle] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromRectangle[data, [area.x, area.y, area.w, area.h]];
ApplyMask[data, mask];
};
SetCP: PROC [context: Context, cp: Pair] ~ {
data: Data ← NARROW[context.data];
data.currentPosition ← ImagerTransform.Transform[cp, data.transformation];
};
GetCP: PROC [context: Context] RETURNS [cp: Pair] ~ {
data: Data ← NARROW[context.data];
cp ← ImagerTransform.InverseTransform[data.currentPosition, data.transformation];
};
SetIntCP: PROC [context: Context, cp: IntPair] ~ {
SetCP[context, [cp.x, cp.y]];
};
GetIntCP: PROC [context: Context] RETURNS [cp: IntPair] ~ {
realCP: Pair ← GetCP[context];
cp.x ← Real.RoundLI[realCP.x];
cp.y ← Real.RoundLI[realCP.y];
};
rasterToRunGroupStorageRatio: INT ← 1;
CharLoadInfo: TYPE = RECORD [sWidth, fWidth: Scaled.Value, mask: Mask, loadRepSize: INT];
MaskAndRunCount: TYPE ~ RECORD [
mask: Mask,
nRuns: INT
];
GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR] RETURNS [ans: MaskAndRunCount] ~ {
nRuns: INT ← 0;
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
Run: PROC [s, fMin: INTEGER, fSize: NAT] ~ {
run[s: s, fMin: fMin, fSize: fSize];
nRuns ← nRuns + 1;
};
font.fontGraphicsClass.maskProc[font, transformation, char, Run];
};
nRuns ← 0;
ans.mask ← ImagerMasks.Create[Runs];
ans.nRuns ← nRuns;
};
FontCompositeTransform: PROC [data: Data, font: UnifiedFonts.FONT] RETURNS [t: Transformation] ~ {
t ← data.transformation;
t.c ← t.f ← 0;
t ← ImagerTransform.Concat[font.actualTransformation, t];
};
fontCache: ImagerFontCache.FontCache ← ImagerFontCache.Create[];
MaskChars: PROC [context: Context, font: UnifiedFonts.FONT, map: PROC[PROC[CHAR]]] ~ {
data: Data ← NARROW[context.data];
transform: Transformation ← FontCompositeTransform[data, font];
fontCode: ImagerFontCache.FontCode ← ImagerFontCache.GetFontCode[transform.a, transform.b, transform.c, transform.d, transform.e, transform.f, font.graphicsKey];
clipper: Mask ← CompositeClipper[data];
sCurrent: Scaled.Value ← ScaledFromReal[data.currentPosition.x];
fCurrent: Scaled.Value ← ScaledFromReal[data.currentPosition.y];
Load: PROC [char: CHAR] = {
loadInfo: REF CharLoadInfo ← NEW[CharLoadInfo];
maskN: MaskAndRunCount ← GetCharMask[font, transform, char];
runGroupSize: INT ← maskN.nRuns * 2 + 2;
rasterSize: INT ← maskN.mask.SSize * INT[(maskN.mask.FSize+15)/16] + 2;
width: Pair ← ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], data.transformation];
loadInfo.mask ← maskN.mask;
SELECT TRUE FROM
maskN.mask.SSize = 0 OR maskN.mask.FSize = 0 => {loadInfo.loadRepSize ← 0};
maskN.mask.FSize > 32*Environment.bitsPerWord OR
runGroupSize*rasterToRunGroupStorageRatio < rasterSize => {loadInfo.loadRepSize ← runGroupSize};
ENDCASE => {loadInfo.loadRepSize ← rasterSize; loadInfo.mask ← loadInfo.mask.Bitmap};
loadInfo.sWidth ← Scaled.FromReal[width.x];
loadInfo.fWidth ← Scaled.FromReal[width.y];
fontCache.LoadCharData[fontCode, char, loadInfo];
};
DoChar: PROC [char: CHAR] = {
sCP: INTEGER ← sCurrent.Round;
fCP: INTEGER ← fCurrent.Round;
loadInfo: REF CharLoadInfo ← LoadInfo[];
LoadInfo: PROC RETURNS [cli: REF CharLoadInfo] ~ INLINE {
cli ← NARROW[
fontCache.GetCharData[fontCode, char
! ImagerFontCache.CacheMiss => {Load[char]; RETRY}
]
];
};
ApplyMask[data, loadInfo.mask.Shift[sCP, fCP]];
sCurrent ← sCurrent.PLUS[loadInfo.sWidth];
fCurrent ← fCurrent.PLUS[loadInfo.fWidth];
};
map[DoChar];
data.currentPosition ← [x: sCurrent.Float, y: fCurrent.Float];
};
MaskChar: PROC [context: Context, font: UnifiedFonts.FONT, char: CHAR] ~ {
MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]};
MaskChars[context, font, MapOneChar];
};
MaskCharacters: PROC [
context: Context,
font: UnifiedFonts.FONT,
characters: REF, -- may be a Rope.ROPE or a REF TEXT
start: INT ← 0,
length: INTLAST[INT]
] ~ {
WITH characters SELECT FROM
rope: ROPE => {
Map: PROC [Char: PROC[CHAR]] = {
Action: PROC [c: CHAR] RETURNS [quit: BOOLFALSE] = {Char[c]};
[] ← rope.Map[start: start, len: length, action: Action];
};
MaskChars[context, font, Map]
};
text: REF TEXT => {
size: INT ← text.length;
rem: INT ← RopeInline.NonNeg[size - RopeInline.NonNeg[start]];
Map: PROC [Char: PROC[CHAR]] = {
FOR i: INT IN [start..start+length) DO
Char[text[i]];
ENDLOOP;
};
IF length > rem THEN length ← rem;
MaskChars[context, font, Map];
};
ENDCASE => IF characters # NIL THEN ERROR;
};
TransferBuffer: PROC [context, source: Context] ~ {
data: Data ← NARROW[context.data];
WITH source.data SELECT FROM
sourceData: Data => {
ImagerMasks.MaskSampled[data.canvas, CompositeClipper[data], sourceData.canvas.InlineShift[data.sOriginView-sourceData.sOriginView, data.fOriginView-sourceData.fOriginView]];
};
ENDCASE => ERROR Imager.IncompatibleContexts;
};
SetColorInvert: PROC [context: Context] ~ {
data: Data ← NARROW[context.data];
data.currentColor ← $XOR;
};
DrawBitmap: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, area: IntRectangle] ~ {
ENABLE UNWIND => NULL;
data: Data ← NARROW[context.data];
IF data.transformation.type = rot90 THEN {
sMin, fMin, sSize, fSize: INTEGER;
[[sMin, fMin, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[area, data.transformation];
scratchBitmapDesc.pointer ← base;
scratchBitmapDesc.rast ← raster;
scratchBitmapDesc.lines ← sSize;
data.canvas.MaskSampled[CompositeClipper[data], [sMin, fMin, sSize, fSize, scratchBitmapDesc]];
}
ELSE {
IF firstBadDrawBitmap THEN {firstBadDrawBitmap ← FALSE; ERROR}
Sorry, not yet.
};
};
firstBadDrawBitmap: BOOLEANTRUE;
SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [implemented: BOOLEAN] ~ {
clipperData: Data ← NARROW[context.data];
implemented ← TRUE;
SELECT op FROM
$OtherScreen => {
swapped: BOOLEAN;
TRUSTED {swapped ← TerminalMultiplex.SelectTerminal[swap]};
TRUSTED {IF UserTerminal.GetState[] = disconnected THEN [] ← UserTerminal.SetState[on]};
clipperData.canvas ← ImagerMasks.LFDisplay[];
TRUSTED {swapped ← TerminalMultiplex.SelectTerminal[swap]};
implemented ← swapped
};
ENDCASE => implemented ← FALSE;
};
MaskFromSurfaceRectangle: PROC [data: Data, box: IntRectangle] RETURNS [Mask] ~ {
deviceBox: IntRectangle ← ImagerTransform.TransformIntRectangle[box, data.surfaceToDevice];
RETURN [ImagerMasks.InlineBox[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]];
};
SetView: PROC [context: Context, box: IntRectangle, halftoneOrigin: IntPair ← [0, 0]] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromSurfaceRectangle[data, box];
deviceOrigin: IntPair ← ImagerTransform.IntTransform[[box.x, box.y], data.surfaceToDevice];
sOriginView: INTEGER ← deviceOrigin.x;
fOriginView: INTEGER ← deviceOrigin.y;
sShift: INTEGER ← sOriginView-data.sOriginView;
fShift: INTEGER ← fOriginView-data.fOriginView;
newHalftoneOrigin: IntPair ← ImagerTransform.IntTransform[halftoneOrigin, data.surfaceToDevice];
IF data.doSaveCount # 0 THEN ERROR; -- Can't change a view with DoSave in progress.
IF data.viewClipper = mask AND sShift = 0 AND fShift = 0 AND newHalftoneOrigin = [data.sOriginBrick, data.fOriginBrick] THEN RETURN;
data.compositeClipperValid ← FALSE;
data.compositeClipper.refRep ← NIL;
IF data.clientClipperPresent THEN {
data.clientClipper ← data.clientClipper.Shift[sShift, fShift];
ccCount ← ccCount + 1;
};
data.viewClipper ← mask;
data.currentPosition.x ← data.currentPosition.x + sShift;
data.currentPosition.y ← data.currentPosition.y + fShift;
data.transformation.c ← data.transformation.c + sShift;
data.transformation.f ← data.transformation.f + fShift;
data.viewToDevice.c ← data.viewToDevice.c + sShift;
data.viewToDevice.f ← data.viewToDevice.f + fShift;
data.sOriginView ← sOriginView;
data.fOriginView ← fOriginView;
[data.sOriginBrick, data.fOriginBrick] ← newHalftoneOrigin;
};
ClipView: PROC [context: Context, box: IntRectangle, exclude: BOOLEAN] ~ {
data: Data ← NARROW[context.data];
mask: Mask ← MaskFromSurfaceRectangle[data, box];
data.compositeClipperValid ← FALSE;
data.compositeClipper.refRep ← NIL;
data.viewClipper ←
IF exclude THEN data.viewClipper.Difference[mask]
ELSE data.viewClipper.And[mask];
};
GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ← NARROW[context.data];
deviceToSurface: Transformation ← ImagerTransform.Invert[data.surfaceToDevice];
RETURN [ImagerTransform.TransformIntRectangle[[data.canvas.sMin, data.canvas.fMin, data.canvas.sSize, data.canvas.fSize], deviceToSurface]]
};
GetViewBounds: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ← NARROW[context.data];
deviceToSurface: Transformation ← ImagerTransform.Invert[data.surfaceToDevice];
RETURN [ImagerTransform.TransformIntRectangle[[data.viewClipper.sMin, data.viewClipper.fMin, data.viewClipper.sSize, data.viewClipper.fSize], deviceToSurface]]
};
MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] RETURNS [BOOLEAN] ~ {
data: Data ← NARROW[context.data];
bitmap: REF ImagerMasks.MaskRep.bitmap;
sSizeCanvas, fSizeCanvas: NAT;
sTranslate, fTranslate: INTEGER;
sMinSource, fMinSource, sMinDest, fMinDest: INTEGER;
sSize, fSize: INTEGER;
WITH data.canvas.refRep SELECT FROM
b: REF ImagerMasks.MaskRep.bitmap => bitmap ← b;
ENDCASE => ERROR;
sSizeCanvas ← MIN[data.canvas.sSize, bitmap.lines];
fSizeCanvas ← MIN[data.canvas.fSize, bitmap.rast*Environment.bitsPerWord];
[[sTranslate, fTranslate]] ← ImagerTransform.TransformIntVec[[dest.x-source.x, dest.y-source.y], data.surfaceToDevice];
[[sMinSource, fMinSource, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[source, data.surfaceToDevice];
sMinSource ← sMinSource - data.canvas.sMin;
fMinSource ← fMinSource - data.canvas.fMin;
sMinDest ← sMinSource + sTranslate;
fMinDest ← fMinSource + fTranslate;
IF sMinDest < 0 THEN {
d: INTEGER ← - sMinDest;
sSize ← sSize - d;
sMinSource ← sMinSource + d;
sMinDest ← sMinDest + d;
};
IF sMinSource < 0 THEN {
d: INTEGER ← - sMinSource;
sSize ← sSize - d;
sMinSource ← sMinSource + d;
sMinDest ← sMinDest + d;
};
IF fMinDest < 0 THEN {
d: INTEGER ← - fMinDest;
fSize ← fSize - d;
fMinSource ← fMinSource + d;
fMinDest ← fMinDest + d;
};
IF fMinSource < 0 THEN {
d: INTEGER ← - fMinSource;
fSize ← fSize - d;
fMinSource ← fMinSource + d;
fMinDest ← fMinDest + d;
};
IF sMinDest + sSize > sSizeCanvas THEN {
sSize ← sSizeCanvas - sMinDest
};
IF sMinSource + sSize > sSizeCanvas THEN {
sSize ← sSizeCanvas - sMinSource
};
IF fMinDest + fSize > fSizeCanvas THEN {
fSize ← fSizeCanvas - fMinDest
};
IF fMinSource + fSize > data.canvas.fMin + fSizeCanvas THEN {
fSize ← fSizeCanvas - fMinSource
};
IF sSize > 0 AND fSize > 0 THEN TRUSTED {
Blt[bitmap.pointer, bitmap.rast, sMinSource, fMinSource, sMinDest, fMinDest, sSize, fSize]
};
RETURN [TRUE];
};
Blt: UNSAFE PROC [dbase: LONG POINTER, drast, sMinSource, fMinSource, sMinDest, fMinDest, sSize, fSize: NAT] ~ UNCHECKED INLINE {
bpl: INTEGER ← drast * Environment.bitsPerWord;
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];
bb^ ← [dst: [word: NIL, bit: 0], dstBpl: 0,
src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0,
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]];
IF (fMinSource+fSize)>fMinDest AND (fMinDest+fSize)>fMinSource AND (sMinSource+sSize)>sMinDest AND (sMinDest+sSize)>sMinSource THEN {
bb.flags.disjoint ← FALSE; -- the rectangles overlap
IF sMinDest=sMinSource THEN bb.flags.disjointItems ← FALSE; -- so do the items
IF sMinDest>sMinSource OR (sMinDest=sMinSource AND fMinDest>fMinSource) THEN { -- reverse direction
bb.flags.direction ← backward; bpl ← -bpl;
sMinSource ← sMinSource + (sSize-1); sMinDest ← sMinDest + (sSize-1);
};
};
bb.dst.word ← dbase + Inline.LongMult[sMinDest, drast] + fMinDest/16; bb.dst.bit ← fMinDest MOD 16;
bb.src.word ← dbase + Inline.LongMult[sMinSource, drast] + fMinSource/16; bb.src.bit ← fMinSource MOD 16;
bb.dstBpl ← bb.srcDesc.srcBpl ← bpl;
bb.width ← fSize; bb.height ← sSize;
BitBlt.BITBLT[bb];
};
LFDisplayClass: REF Imager.ClassRep ← NEW [Imager.ClassRep ← [
deviceType: $LFDisplay,
Init: Init,
DoSaveAll: DoSaveAll,
DoSave: DoSave,
Flush: Flush,
Close: Close,
TranslateT: TranslateT,
RotateT: RotateT,
ScaleT: ScaleT,
ConcatT: ConcatT,
GetT: GetT,
SetT: SetT,
IntTranslateT: IntTranslateT,
IntScaleT: IntScaleT,
GetClipper: GetClipper,
SetClipper: SetClipper,
ClipPath: ClipPath,
ClipRectangle: ClipRectangle,
TestRectangle: TestRectangle,
ClipIntRectangle: ClipIntRectangle,
TestIntRectangle: TestIntRectangle,
DoWithoutClipping: DoWithoutClipping,
SetColor: SetColor,
GetColor: GetColor,
MaskStroke: MaskStroke,
MaskFill: MaskFill,
MaskPixel: MaskPixel,
MaskBits: MaskBits,
MaskSegment: MaskSegment,
MaskIntSegment: MaskIntSegment,
MaskThinStroke: MaskThinStroke,
MaskRectangle: MaskRectangle,
MaskIntRectangle: MaskIntRectangle,
SetCP: SetCP,
GetCP: GetCP,
SetIntCP: SetIntCP,
GetIntCP: GetIntCP,
MaskChar: MaskChar,
MaskCharacters: MaskCharacters,
TransferBuffer: TransferBuffer,
Reset: Reset,
SetView: SetView,
ClipView: ClipView,
GetSurfaceBounds: GetSurfaceBounds,
GetViewBounds: GetViewBounds,
MoveSurfaceRectangle: MoveSurfaceRectangle,
SetColorInvert: SetColorInvert,
DrawBitmap: DrawBitmap,
SpecialOp: SpecialOp
]];
ImagerRegistration.RegisterDevice[LFDisplayClass];
END.