ImagerLFImpl.mesa
Michael Plass, June 19, 1984 1:19:53 pm PDT
Doug Wyatt, November 23, 1983 2:13 pm
DIRECTORY
Basics,
Font,
Imager,
ImagerBasic,
ImagerBrick,
ImagerConic,
ImagerDefault,
ImagerFontCache,
ImagerFrameBuffer,
ImagerHalftone,
ImagerLF,
ImagerManhattan,
ImagerPixelMaps,
ImagerPrivate,
ImagerScanConverter,
ImagerStroke,
ImagerTransform,
ImagerMasks,
Real,
Rope,
RefText,
Scaled;
ImagerLFImpl: CEDAR MONITOR
IMPORTS Imager, ImagerConic, ImagerDefault, ImagerFontCache, ImagerFrameBuffer, ImagerHalftone, ImagerManhattan, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, ImagerMasks, Real, Rope, RefText, Scaled
~ BEGIN OPEN ImagerBasic;
StatsRecord: TYPE ~ RECORD [
loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT ← 0];
stats: StatsRecord ← [];
Stats: PROC RETURNS [StatsRecord] ~ { x: StatsRecord ~ stats; stats ← []; RETURN[x] };
FONT: TYPE ~ Font.FONT;
PixelMap: TYPE ~ ImagerPixelMaps.PixelMap;
Tile: TYPE ~ ImagerPixelMaps.Tile;
SpecialColor: TYPE ~ REF ColorRep[special];
Name: TYPE ~ ImagerPrivate.Name;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
TransformationRec: TYPE ~ ImagerTransform.TransformationRec;
Context: TYPE ~ Imager.Context;
State: TYPE ~ ImagerDefault.State;
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ ImagerLF.DataRep;
SurfaceOriginS: PROC [data: Data] RETURNS [s: INTEGER] ~ INLINE {
RETURN [data.canvas.sSize]
};
CompositeT: PROC [data: Data, state: State] RETURNS [TransformationRec] ~ {
concatenates T with the current view-to-device transformation
t: TransformationRec ~ state.T.Contents;
RETURN [[
a: -t.d, d: t.a,
b: -t.e, e: t.b,
c: -t.f+SurfaceOriginS[data]-data.viewOrigin.y,
f: t.c+data.viewOrigin.x
]]
};
ViewToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ {
RETURN [[
a: 0, d: 1,
b: -1, e: 0,
c: SurfaceOriginS[data]-data.viewOrigin.y,
f: data.viewOrigin.x
]]
};
SurfaceToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ {
RETURN [[
a: 0, d: 1,
b: -1, e: 0,
c: SurfaceOriginS[data],
f: 0
]]
};
DevicePair: TYPE ~ RECORD [s, f: Scaled.Value];
TransformDeviceToViewVec: PROC [d: DevicePair] RETURNS [v: Pair] ~ {
v ← [x: Scaled.Float[d.f], y: -Scaled.Float[d.s]];
};
GetDeviceCP: PROC [data: Data, state: State] RETURNS [DevicePair] ~ {
RETURN [[
s: ScaledFromReal[-state.cpy].PLUS[Scaled.FromInt[SurfaceOriginS[data]-data.viewOrigin.y]],
f: ScaledFromReal[state.cpx].PLUS[Scaled.FromInt[data.viewOrigin.x]]
]]
};
SetDeviceCP: PROC [data: Data, state: State, cp: DevicePair] ~ {
state.cpy ← -Scaled.Float[cp.s.MINUS[Scaled.FromInt[SurfaceOriginS[data]-data.viewOrigin.y]]];
state.cpx ← Scaled.Float[cp.f.MINUS[Scaled.FromInt[data.viewOrigin.x]]];
};
Init: PROC [context: Context, info: REF] ~ {
tileStorage: PixelMap ~ ImagerPixelMaps.Create[lgBitsPerPixel: 0, bounds: [0, 0, 16, 16]];
data: Data ~ NEW[DataRep];
context.data ← data;
data.tile ← [sOrigin: 0, fOrigin: 0, sSize: 16, fSize: 16, phase: 0, refRep: tileStorage.refRep];
data.tileSamples ← MakeTileSamples[16, 16];
data.devicePath ← NIL;
WITH info SELECT FROM
pm: REF PixelMap => data.canvas ← pm^;
ENDCASE => TRUSTED {data.canvas ← ImagerFrameBuffer.LFDisplay[]};
ImagerDefault.InitState[context];
SetViewOrigin[context, [0, 0]];
SetViewBox[context, GetSurfaceBounds[context]];
Reset[context];
};
pixelsPerInch: REAL ~ 72.0;
mmPerInch: REAL ~ 25.4;
inchesPerMeter: REAL ~ 1000.0/mmPerInch;
pixelsPerMeter: REAL ← pixelsPerInch*inchesPerMeter;
Reset: PROC [context: Context] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
ImagerDefault.Reset[context];
ImagerDefault.ScaleT[context, pixelsPerMeter];
state.fieldXMax ← state.mediumXSize ← data.canvas.fSize/pixelsPerMeter;
state.fieldYMax ← state.mediumYSize ← data.canvas.sSize/pixelsPerMeter;
data.compositeClipper ← NIL;
data.cachedColor ← NIL;
};
MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: SampledColor] ~ {
tileSamples ← NEW[ColorRep[sampled]];
tileSamples.transparent ← FALSE;
tileSamples.pa ← NEW[PixelArrayRep];
tileSamples.m ← tileSamples.pa.m ← ImagerTransform.Rotate[0];
tileSamples.colorOperator ← $Intensity;
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];
};
ConstantGet: PROC [self: PixelArray, buffer: 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;
};
SetTileSamples: PROC [tileSamples: SampledColor, intensity: [0..255]] ~ {
WITH tileSamples.pa.data SELECT FROM
n: REF NAT => n^ ← intensity;
ENDCASE => ERROR;
};
DevicePathFromViewPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] RETURNS [ImagerScanConverter.DevicePath] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
~ {
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (s, f)
-p.y + SurfaceOriginS[data]-data.viewOrigin.y,
p.x + data.viewOrigin.x
]]};
Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp ← q };
Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp ← q };
Xcurve: PROC [p1, p2, p3: Pair] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3];
curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp ← q3 };
Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]};
Xconic: PROC [p1, p2: Pair, r: REAL] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2];
ImagerConic.ToCurves[lp, q1, q2, r, Curve];
lp ← q2;
};
lp: Pair;
pathMap[pathData, Xmove, Xline, Xcurve, Xconic];
};
RETURN [ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.canvas.BoundedWindow]];
};
DevicePathFromPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
~ {
m: TransformationRec ~ CompositeT[data, state];
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (s, f)
m.a * p.x + m.b * p.y + m.c,
m.d * p.x + m.e * p.y + m.f
]]};
Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp ← q };
Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp ← q };
Xcurve: PROC [p1, p2, p3: Pair] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3];
curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp ← q3 };
Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]};
Xconic: PROC [p1, p2: Pair, r: REAL] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2];
ImagerConic.ToCurves[lp, q1, q2, r, Curve];
lp ← q2;
};
lp: Pair;
pathMap[pathData, Xmove, Xline, Xcurve, Xconic];
};
data.devicePath ← ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.canvas.BoundedWindow, scratch: data.devicePath];
};
DevicePathFromStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
data.devicePath ← ImagerStroke.DevicePathFromStroke[
pathMap: pathMap,
pathData: pathData,
clientToDevice: CompositeT[data, state].FromRec,
width: IF LOOPHOLE[strokeWidth, LONG CARDINAL] # LOOPHOLE[Imager.defaultStrokeWidth, LONG CARDINAL] THEN strokeWidth ELSE state.strokeWidth,
strokeEnd: IF strokeEnd # nil THEN strokeEnd ELSE SELECT state.strokeEnd FROM
0 => square,
1 => butt,
2 => round,
ENDCASE => nil,
closed: closed,
clipBox: data.canvas.BoundedWindow,
scratch: data.devicePath
];
};
Floor: PROC [real: REAL] RETURNS [int: INT] ~ {
int ← Real.RoundLI[real];
WHILE int < real DO int ← int + 1 ENDLOOP;
WHILE int > real DO int ← int - 1 ENDLOOP;
};
Ceiling: PROC [real: REAL] RETURNS [int: INT] ~ {
int ← Real.RoundLI[real];
WHILE int > real DO int ← int - 1 ENDLOOP;
WHILE int < real DO int ← int + 1 ENDLOOP;
};
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]]
};
CompositeClipper: PROC [context: Context] RETURNS [ManhattanPolygon] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
mp: ManhattanPolygon ← data.compositeClipper;
IF mp = NIL OR data.clientClipper # state.clipper THEN {
ConcatClippers: PROC [l: LIST OF ClientClipperItem] ~ {
IF l#NIL THEN {
t1, t2: ManhattanPolygon;
ConcatClippers[l.rest];
t1 ← ImagerScanConverter.ConvertToManhattanPolygon[DevicePathFromViewPath[context: context, pathMap: l.first.pathMap, pathData: l.first.pathData], [data.canvas.sOrigin+data.canvas.sMin, data.canvas.fOrigin+data.canvas.fMin, data.canvas.sSize, data.canvas.fSize]];
t2 ← mp;
mp ← IF l.first.exclude THEN mp.Difference[t1] ELSE mp.Intersection[t1];
ImagerManhattan.Destroy[t1];
ImagerManhattan.Destroy[t2];
};
};
ImagerManhattan.Destroy[data.compositeClipper];
data.clientClipper ← state.clipper;
mp ← ImagerManhattan.Copy[data.viewClipper];
ConcatClippers[data.clientClipper];
data.compositeClipper ← mp;
};
RETURN [mp]
};
lfBrick: ImagerHalftone.DeviceBrick ← ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255];
CurrentColor: PROC [context: Context] RETURNS [color: Color] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ {
FOR s: INTEGER IN [data.tile.sOrigin..data.tile.sOrigin + data.tile.sSize) DO
run[s, data.tile.fOrigin, data.tile.fSize];
ENDLOOP;
};
IF data.cachedColor = state.color THEN RETURN [data.cachedColor];
color ← data.cachedColor ← state.color;
IF color = Imager.black OR color = Imager.white THEN RETURN;
WITH color SELECT FROM
constantColor: ConstantColor => {
intensity: INT ← constantColor.Y;
intensity ← (intensity + 127)/(LAST[CARDINAL]/255);
SetTileSamples[data.tileSamples, intensity];
ImagerHalftone.Halftone[
dest: [sOrigin: data.tile.sOrigin, fOrigin: data.tile.fOrigin, sMin: 0, fMin: 0, sSize: data.tile.sSize, fSize: data.tile.fSize, refRep: data.tile.refRep],
runs: Runs,
source: data.tileSamples.pa,
transformation: ImagerTransform.Rotate[0],
deviceBrick: lfBrick
];
};
specialColor: SpecialColor => {
WITH specialColor.ref SELECT FROM
stipple: REF CARDINAL =>
data.tile ← ImagerPixelMaps.TileFromStipple[stipple: stipple^, scratch: data.tile.refRep];
atom: ATOM => {
SELECT atom FROM
$XOR => NULL;
ENDCASE => ERROR Imager.Error[$UnknownSpecialColor];
};
ENDCASE => ERROR Imager.Error[$UnknownSpecialColor];
};
sampledColor: SampledColor => NULL;
ENDCASE => state.color ← Imager.black;
};
ApplyMask: PROC [context: Context, mask: REF, sTranslate, fTranslate: INTEGER ← 0] ~ {
state: State ~ NARROW[context.state];
IF state.noImage = 0 THEN {
data: Data ~ NARROW[context.data];
color: Color ~ CurrentColor[context];
clipper: ManhattanPolygon ~ CompositeClipper[context];
SELECT color FROM
Imager.black => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 1, [null, null], sTranslate, fTranslate];
Imager.white => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 0, [null, null], sTranslate, fTranslate];
Imager.XOR => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 1, [xor, null], sTranslate, fTranslate];
ENDCASE => {
WITH color SELECT FROM
sampledColor: SampledColor => {
Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ {
ImagerMasks.GenerateRuns[mask, clipper, run, sTranslate, fTranslate];
};
transform: Transformation ← sampledColor.pa.m.Concat[sampledColor.m].Concat[ViewToDevice[data].FromRec];
invertOutput: BOOLEANFALSE;
customBrick: ImagerBrick.Brick ← NARROW[Imager.GetProp[context, $CustomBrick]];
deviceBrick: ImagerHalftone.DeviceBrick;
IF customBrick # NIL THEN
deviceBrick ← ImagerHalftone.MakeDeviceBrick[customBrick, sampledColor.pa.maxSampleValue]
ELSE {
IF data.deviceBrickMaxSampleValue # sampledColor.pa.maxSampleValue THEN {
data.deviceBrickMaxSampleValue ← sampledColor.pa.maxSampleValue;
data.deviceBrick ← ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, sampledColor.pa.maxSampleValue];
};
deviceBrick ← data.deviceBrick;
};
SELECT sampledColor.colorOperator FROM
$SampledBlack => invertOutput ← TRUE;
$Intensity => NULL;
ENDCASE => Imager.Error[$UnknownColorModel];
ImagerHalftone.Halftone[data.canvas, Runs, sampledColor.pa, transform, deviceBrick, invertOutput, sampledColor.transparent];
};
ENDCASE => { -- constant (other than black or white) or stipple
tile: ImagerPixelMaps.Tile ← data.tile;
tile.sOrigin ← tile.sOrigin + SurfaceOriginS[data]-data.viewOrigin.y;
tile.fOrigin ← tile.fOrigin + data.viewOrigin.x;
ImagerMasks.ApplyTile[mask, clipper, data.canvas, tile, [null, null], sTranslate, fTranslate];
};
};
};
};
MaskStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL, strokeEnd: StrokeEnd] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromStroke[context, pathMap, pathData, strokeWidth, strokeEnd, FALSE];
ApplyMask[context, data.devicePath];
};
MaskStrokeClosed: PROC [context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromStroke[context, pathMap, pathData, strokeWidth, nil, TRUE];
ApplyMask[context, data.devicePath];
};
MaskFill: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
DevicePathFromPath[context, pathMap, pathData];
ApplyMask[context, data.devicePath];
};
specialCaseRectangles: BOOLEANTRUE;
MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
trans: TransformationRec ~ CompositeT[data, state];
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: REAL ~ trans.b * y + trans.c;
s1: REAL ~ trans.b * (y+h) + trans.c;
f0: REAL ~ trans.d * x + trans.f;
f1: REAL ~ trans.d * (x+w) + trans.f;
sMin: INTEGER ~ Floor[MAX[MIN[s0, s1], -LAST[INTEGER]/2]];
sMax: INTEGER ~ Ceiling[MIN[MAX[s0, s1], LAST[INTEGER]/2]];
fMin: INTEGER ~ Floor[MAX[MIN[f0, f1], -LAST[INTEGER]/2]];
fMax: INTEGER ~ Ceiling[MIN[MAX[f0, f1], LAST[INTEGER]/2]];
t1: ManhattanPolygon;
IF sMax<=sMin OR fMax<=fMin THEN RETURN;
t1 ← ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]];
ApplyMask[context, t1, 0, 0];
ImagerManhattan.Destroy[t1];
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
MaskFill[context, PathMap, NIL];
};
};
IntegerMaskRectangle: PROC [context: Context, x, y, w, h: INTEGER] ~ {
MaskRectangle[context, x, y, w, h];
};
MaskPixel: PROC [context: Context, pa: PixelArray] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
trans: Transformation ~ CompositeT[data, state].FromRec;
mask: REF ← ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]];
ApplyMask[context, mask, 0, 0];
mask ← NIL;
};
rasterToRunGroupStorageRatio: INT ← 1;
CharLoadInfo: TYPE ~ RECORD[sWidth, fWidth: Scaled.Value, mask: REF];
GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR]
RETURNS [ImagerManhattan.Polygon] ~ {
Runs: PROC [
run: PROC [sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC [timesToRepeatScanline: NAT]] ~ {
Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
run[sMin: sMin, fMin: fMin, fSize: fSize];
};
font.fontGraphicsClass.maskProc[font, transformation, char, Run];
};
RETURN [ImagerManhattan.CreateFromRuns[Runs]]
};
FontCompositeTransform: PROC [context: Context, font: FONT] RETURNS [t: TransformationRec] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
r: TransformationRec ← CompositeT[data, state];
r.c ← r.f ← 0;
t ← ImagerTransform.Concat[font.actualTransformation, r.FromRec].Contents;
};
fontCache: ImagerFontCache.FontCache ← ImagerFontCache.Create[];
LoadCharData: PROC [self: ImagerFontCache.FontObject, charCode: CARDINAL]
RETURNS [charData: REF, memoryCost: INT] ~ {
font: FONT ~ NARROW[self.fontAnyData];
char: CHAR ~ 0C+charCode; -- does this bounds check properly?
transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device
clientTransform: Transformation ~ font.actualTransformation.Invert.Concat[transform];
loadInfo: REF CharLoadInfo ← NEW[CharLoadInfo];
mask: ImagerManhattan.Polygon ← GetCharMask[font, transform, char];
nBoxes: INT ~ ImagerManhattan.CountBoxes[mask];
bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask];
boxesSize: INT ← 7 * nBoxes;
rasterSize: INT ← bb.sSize * INT[(bb.fSize+15)/16] + 2;
width: Pair ← ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char] , clientTransform];
IF bb.fSize > 32*Basics.bitsPerWord OR boxesSize*rasterToRunGroupStorageRatio < rasterSize THEN loadInfo.mask ← mask
ELSE {
pixelMap: PixelMap ← ImagerPixelMaps.Create[0, bb];
ImagerPixelMaps.Clear[pixelMap];
FOR l: LIST OF DeviceRectangle ← mask, l.rest UNTIL l = NIL DO
ImagerPixelMaps.Fill[pixelMap, l.first, 1];
ENDLOOP;
loadInfo.mask ← ImagerMasks.FromBitmap[pixelMap];
ImagerManhattan.Destroy[mask]; mask ← NIL;
};
loadInfo.sWidth ← Scaled.FromReal[width.x];
loadInfo.fWidth ← Scaled.FromReal[width.y];
RETURN[loadInfo, 0];
};
ShowChar: PROC [context: Context, char: CHAR, font: FONT] ~ {
text: REF TEXT ← RefText.ObtainScratch[1];
text[0] ← char;
text.length ← 1;
ShowCharacters[context, text, font, 0, 1];
RefText.ReleaseScratch[text];
};
ShowCharacters: PROC [
context: Context,
characters: REF, -- may be a ROPE or a REF TEXT
font: FONT,
start: INT ← 0,
length: INTLAST[INT]
] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
showVec: FONTIF font = NIL THEN NARROW[state.showVec] ELSE font;
transform: TransformationRec ← FontCompositeTransform[context, showVec];
fontCode: ImagerFontCache.FontCode ← ImagerFontCache.GetFontCode[[
CharDataProc: LoadCharData,
r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f,
fontScalarData: 0, -- unused
fontAnyData: showVec]];
noImage: BOOLEAN ~ state.noImage # 0;
residual: INT;
TooBigForFixed: ERROR ~ CODE;
WITH characters SELECT FROM
rope: Rope.ROPE => residual ← Rope.Size[rope];
text: REF TEXT => residual ← MIN[text.length, text.maxLength];
ENDCASE => Imager.Error[$MustBeRopeOrRefText];
start ← MIN[residual, MAX[0,start]];
length ← MIN[residual-start, MAX[length, 0]];
residual ← length;
WHILE residual > 0 DO
{ENABLE TooBigForFixed => GOTO TryAgain;
IF state.correctPass = 0 AND ABS[state.cpx] < NAT.LAST AND ABS[state.cpy] < NAT.LAST THEN {
DoChar: PROC [charCode: CARDINAL, charData: REF] ~ {
loadInfo: REF CharLoadInfo ~ NARROW[charData];
sDelta: Scaled.Value ← loadInfo.sWidth;
fDelta: Scaled.Value ← loadInfo.fWidth;
IF ABS[cp.s.Floor] >= NAT.LAST OR ABS[cp.s.Floor] >= NAT.LAST THEN {SetDeviceCP[data, state, cp]; ERROR TooBigForFixed};
IF NOT noImage THEN ApplyMask[context, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]];
IF charCode = ORD[' ] AND state.amplifySpace # 1.0 THEN {
sDelta ← Scaled.FromReal[state.amplifySpace*Scaled.Float[sDelta]];
fDelta ← Scaled.FromReal[state.amplifySpace*Scaled.Float[fDelta]];
};
cp.s ← cp.s.PLUS[sDelta];
cp.f ← cp.f.PLUS[fDelta];
residual ← residual - 1;
};
cp: DevicePair ← GetDeviceCP[data, state];
ImagerFontCache.GetStringData[DoChar, fontCache, fontCode, characters, start, length];
SetDeviceCP[data, state, cp];
}
ELSE {
CorrectingDoChar: PROC [charCode: CARDINAL, charData: REF] ~ {
loadInfo: REF CharLoadInfo ~ NARROW[charData];
delta: Pair ← TransformDeviceToViewVec[[loadInfo.sWidth, loadInfo.fWidth]];
IF NOT noImage THEN {
cp: DevicePair ← GetDeviceCP[data, state];
ApplyMask[context, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]];
};
IF charCode = ORD[' ] THEN {
IF state.amplifySpace # 1.0 THEN {
delta.x ← delta.x * state.amplifySpace;
delta.y ← delta.y * state.amplifySpace;
};
ImagerDefault.CorrectSpaceView[context, delta];
}
ELSE {
ImagerDefault.CorrectMask[context];
};
state.cpx ← state.cpx + delta.x;
state.cpy ← state.cpy + delta.y;
residual ← residual - 1;
};
ImagerFontCache.GetStringData[CorrectingDoChar, fontCache, fontCode, characters, start, length];
};
EXITS TryAgain => {start ← start + (length-residual); length ← residual};
};
ENDLOOP;
};
ManhattanPolygonFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [LIST OF DeviceRectangle] ~ {
data: Data ~ NARROW[context.data];
s0: INTEGER ← SurfaceOriginS[data]-box.y;
s1: INTEGER ← SurfaceOriginS[data]-(box.y+box.h);
f0: INTEGER ← box.x;
f1: INTEGER ← box.x+box.w;
RETURN [ImagerManhattan.CreateFromBox[[MIN[s0, s1], MIN[f0, f1], ABS[s1-s0], ABS[f1-f0]]]];
};
SetViewOrigin: PROC [context: Context, viewOrigin: IntPair] ~ {
data: Data ← NARROW[context.data];
data.viewOrigin ← viewOrigin;
};
GetViewOrigin: PROC [context: Context] RETURNS [viewOrigin: IntPair] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.viewOrigin];
};
SetViewBox: PROC [context: Context, viewBox: IntRectangle] ~ {
data: Data ← NARROW[context.data];
surfaceBox: IntRectangle ← [
x: viewBox.x+data.viewOrigin.x,
y: viewBox.y+data.viewOrigin.y,
w: viewBox.w,
h: viewBox.h
];
mask: ImagerManhattan.Polygon ← ManhattanPolygonFromSurfaceRectangle[context, surfaceBox];
ImagerManhattan.Destroy[data.compositeClipper];
data.compositeClipper ← NIL;
ImagerManhattan.Destroy[data.viewClipper];
data.viewClipper ← mask;
};
GetViewBox: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ~ NARROW[context.data];
deviceBox: DeviceRectangle ← ImagerManhattan.BoundingBox[data.viewClipper];
surfaceBox: IntRectangle ← [
x: deviceBox.fMin,
y: SurfaceOriginS[data]-(deviceBox.sMin+deviceBox.sSize),
w: deviceBox.fSize,
h: deviceBox.sSize
];
RETURN[[
x: surfaceBox.x-data.viewOrigin.x,
y: surfaceBox.y-data.viewOrigin.y,
w: surfaceBox.w,
h: surfaceBox.h
]];
};
ClipView: PROC [context: Context, clipBox: IntRectangle, exclude: BOOLEAN] ~ {
data: Data ← NARROW[context.data];
surfaceBox: IntRectangle ← [
x: clipBox.x+data.viewOrigin.x,
y: clipBox.y+data.viewOrigin.y,
w: clipBox.w,
h: clipBox.h
];
newBox: ImagerManhattan.Polygon ← ManhattanPolygonFromSurfaceRectangle[context, surfaceBox];
old: ImagerManhattan.Polygon ← data.viewClipper;
ImagerManhattan.Destroy[data.compositeClipper];
data.compositeClipper ← NIL;
data.viewClipper ←
IF exclude THEN ImagerManhattan.Difference[old, newBox]
ELSE ImagerManhattan.Intersection[old, newBox];
newBox.rest ← old;
ImagerManhattan.Destroy[newBox];
};
MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ {
data: Data ~ NARROW[context.data];
m: Transformation ~ SurfaceToDevice[data].FromRec;
sMinDest, fMinDest, sSize, fSize: INTEGER;
shift: Pair ← ImagerTransform.TransformVec[[dest.x-source.x, dest.y-source.y], m];
[[sMinDest, fMinDest, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[[dest.x, dest.y, source.x, source.h], m];
data.canvas.Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data.canvas.ShiftMap[Real.RoundI[shift.x], Real.RoundI[shift.y]]];
};
TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ {
data: Data ~ NARROW[context.data];
state: State ~ NARROW[context.state];
trans: TransformationRec ~ CompositeT[data, state];
manhattanPolygon: ImagerManhattan.Polygon ← NIL;
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: REAL ~ trans.b * y + trans.c;
s1: REAL ~ trans.b * (y+h) + trans.c;
f0: REAL ~ trans.d * x + trans.f;
f1: REAL ~ trans.d * (x+w) + trans.f;
sMin: INTEGER ~ Floor[MAX[MIN[s0, s1], -LAST[INTEGER]/2]];
sMax: INTEGER ~ Ceiling[MIN[MAX[s0, s1], LAST[INTEGER]/2]];
fMin: INTEGER ~ Floor[MAX[MIN[f0, f1], -LAST[INTEGER]/2]];
fMax: INTEGER ~ Ceiling[MIN[MAX[f0, f1], LAST[INTEGER]/2]];
IF sMax<=sMin OR fMax<=fMin THEN RETURN [invisible];
manhattanPolygon ← ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]];
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
DevicePathFromPath[context, PathMap, NIL];
manhattanPolygon ← ImagerScanConverter.ConvertToManhattanPolygon[data.devicePath, data.canvas.BoundedWindow];
};
visibility ← ImagerManhattan.IsVisible[manhattanPolygon, CompositeClipper[context]];
ImagerManhattan.Destroy[manhattanPolygon];
};
GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ~ NARROW[context.data];
deviceToSurface: Transformation ~ ImagerTransform.Invert[SurfaceToDevice[data].FromRec];
RETURN [ImagerTransform.TransformIntRectangle[[data.canvas.sMin, data.canvas.fMin, data.canvas.sSize, data.canvas.fSize], deviceToSurface]]
};
SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [REF] ~ {
SELECT op FROM
ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp];
};
LFDisplayClass: ImagerPrivate.Class ← NEW [ImagerPrivate.ClassRep ← [
deviceType: $LFDisplay,
Init: Init,
ISet: ImagerDefault.ISet,
ISetReal: ImagerDefault.ISetReal,
ISetInt: ImagerDefault.ISetInt,
SetSampledColor: ImagerDefault.SetSampledColor,
SetSampledBlack: ImagerDefault.SetSampledBlack,
DoSave: ImagerDefault.DoSave,
DoSaveAll: ImagerDefault.DoSaveAll,
ConcatT: ImagerDefault.ConcatT,
TranslateT: ImagerDefault.TranslateT,
RotateT: ImagerDefault.RotateT,
ScaleT: ImagerDefault.ScaleT,
Scale2T: ImagerDefault.Scale2T,
Move: ImagerDefault.Move,
Trans: ImagerDefault.Trans,
SetXY: ImagerDefault.SetXY,
IntegerSetXY: ImagerDefault.IntegerSetXY,
SetXYRel: ImagerDefault.SetXYRel,
IntegerSetXYRel: ImagerDefault.IntegerSetXYRel,
GetCP: ImagerDefault.GetCP,
GetCPRounded: ImagerDefault.GetCPRounded,
MaskFill: MaskFill,
MaskStroke: MaskStroke,
MaskStrokeClosed: MaskStrokeClosed,
MaskVector: ImagerDefault.MaskVector,
IntegerMaskVector: ImagerDefault.IntegerMaskVector,
MaskRectangle: MaskRectangle,
IntegerMaskRectangle: IntegerMaskRectangle,
StartUnderline: ImagerDefault.StartUnderline,
MaskUnderline: ImagerDefault.MaskUnderline,
IntegerMaskUnderline: ImagerDefault.IntegerMaskUnderline,
MaskPixel: MaskPixel,
ClipOutline: ImagerDefault.ClipOutline,
ExcludeOutline: ImagerDefault.ExcludeOutline,
ClipRectangle: ImagerDefault.ClipRectangle,
ExcludeRectangle: ImagerDefault.ExcludeRectangle,
IntegerClipRectangle: ImagerDefault.IntegerClipRectangle,
IntegerExcludeRectangle: ImagerDefault.IntegerExcludeRectangle,
ShowChar: ShowChar,
ShowCharacters: ShowCharacters,
CorrectMask: ImagerDefault.CorrectMask,
CorrectSpace: ImagerDefault.CorrectSpace,
SetCorrectMeasure: ImagerDefault.SetCorrectMeasure,
SetCorrectTolerance: ImagerDefault.SetCorrectTolerance,
Space: ImagerDefault.Space,
IntegerSpace: ImagerDefault.IntegerSpace,
Correct: ImagerDefault.Correct,
Reset: Reset,
SetViewOrigin: SetViewOrigin,
GetViewOrigin: GetViewOrigin,
SetViewBox: SetViewBox,
GetViewBox: GetViewBox,
ClipView: ClipView,
MoveSurfaceRectangle: MoveSurfaceRectangle,
TestRectangle: TestRectangle,
GetSurfaceBounds: GetSurfaceBounds,
SpecialOp: SpecialOp
]];
ImagerPrivate.RegisterDevice[LFDisplayClass];
END.