DIRECTORY Atom, Basics, Imager, ImagerBasic, ImagerDisplay, ImagerFrameBuffer, ImagerManhattan, ImagerMasks, ImagerPixelMaps, ImagerPrivate, Terminal, TerminalExtras, ImagerTransform, Scaled;
ImagerExampleDisplayImpl:
CEDAR PROGRAM
IMPORTS Atom, Basics, Imager, ImagerDisplay, ImagerFrameBuffer, ImagerMasks, ImagerPrivate, Terminal, TerminalExtras, ImagerTransform, Scaled ~ BEGIN
This is an example implementation of an Imager display device. It implements an 8-bit-per-pixel gray-level display with no antialiasing.
DisplayClass: TYPE ~ ImagerDisplay.DisplayClass;
DisplayClassRep: TYPE ~ ImagerDisplay.DisplayClassRep;
DisplayData: TYPE ~ ImagerDisplay.DisplayData;
DisplayDataRep: TYPE ~ ImagerDisplay.DisplayDataRep;
DeviceRectangle: TYPE ~ ImagerBasic.DeviceRectangle;
Color:
TYPE ~ ImagerBasic.Color;
ColorRep: TYPE ~ ImagerBasic.ColorRep;
SampledColor: TYPE = REF ColorRep.sampled;
ConstantColor: TYPE = REF ColorRep.constant;
SpecialColor: TYPE = REF ColorRep.special;
Mask: TYPE ~ ImagerMasks.Mask;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
Transformation: TYPE ~ ImagerTransform.Transformation;
Pair: TYPE ~ ImagerBasic.Pair;
PixelMap:
TYPE ~ ImagerPixelMaps.PixelMap;
exampleDisplayClass: ImagerDisplay.DisplayClass ~
NEW[ImagerDisplay.DisplayClassRep ← [
displayType: $Gray8bpp,
viewUnitsPerPixel: 1,
Create: Create,
ApplyMask: ApplyMask,
DoUnderLock: DoUnderLock
]];
Create:
PROC [displayClass: DisplayClass, creationData:
REF]
RETURNS [displayData: DisplayData] ~ {
The creationData is inherited from the Create call. Here we ignore it, but a possible use for it is to point to an alternate buffer.
ppi: REAL ← 42;
WITH creationData
SELECT
FROM
props: Atom.PropList => {
ppiRef: REF ← Atom.GetPropFromList[props, $pixelsPerInch];
WITH ppiRef
SELECT
FROM
r: REF REAL => ppi ← r^;
i: REF INT => ppi ← i^;
ENDCASE => NULL;
};
ENDCASE => NULL;
displayData ← NEW[DisplayDataRep[1]];
displayData.displayClass ← displayClass;
displayData.xRes ← ppi;
displayData.yRes ← ppi;
displayData.rotate ← TRUE;
TRUSTED {displayData[0] ← ImagerFrameBuffer.GrayScaleDisplay8[]};
displayData.surfaceWidth ← displayData[0].fSize;
displayData.surfaceHeight ← displayData[0].sSize;
};
DoUnderLock:
PROC [displayData: DisplayData, action:
PROC, rectangle: DeviceRectangle] ~ {
vt: Terminal.Virtual ← Terminal.Current[];
TerminalExtras.LockColorFrame[
vt: vt,
xmin: MAX[rectangle.fMin, 0],
ymin: MAX[rectangle.sMin, 0],
xmax: MAX[rectangle.fMin+rectangle.fSize, 0],
ymax: MAX[rectangle.sMin+rectangle.sSize, 0]
];
action[! UNWIND => {TerminalExtras.UnlockColorFrame[vt]}];
TerminalExtras.UnlockColorFrame[vt];
};
SurfaceToDevice:
PROC [displayData: DisplayData]
RETURNS [Transformation] ~ {
IF displayData.rotate
THEN
RETURN [ImagerTransform.FromRec[[
a: 0, b: -1, c: displayData.surfaceHeight, d: 1, e: 0, f: 0
]]]
ELSE RETURN [ImagerTransform.Scale[1]]
};
ApplyMask:
PROC [displayData: DisplayData, color: Color, mask: Mask, sTranslate, fTranslate:
INTEGER] ~ {
LockedApplyMask:
PROC ~ {
function: ImagerPixelMaps.Function ← [null, null];
IF color = Imager.
XOR
THEN {
color ← Imager.white;
function ← [xor, null];
};
WITH color
SELECT
FROM
constantColor: ConstantColor => {
pixelValue: [0..255] ← constantColor.Y/256;
ImagerMasks.ApplyConstant[
mask: mask,
clipper: displayData.compositeClipper,
dest: displayData[0],
value: pixelValue,
function: function,
sTranslate: sTranslate,
fTranslate: fTranslate
];
};
sampledColor: SampledColor => {
buffer: ImagerBasic.PixelBuffer ← NEW[ImagerBasic.PixelBufferRep[displayData[0].fSize]];
invertOutput: CARDINAL ← 0;
transform: Transformation ←
ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[
sampledColor.pa.m, sampledColor.m],
ImagerTransform.Translate[displayData.viewOrigin.x, displayData.viewOrigin.y]],
SurfaceToDevice[displayData]];
delta: Pair ← ImagerTransform.InverseTransformVec[[0, 1], transform];
xDelta: Scaled.Value ← Scaled.FromReal[delta.x];
yDelta: Scaled.Value ← Scaled.FromReal[delta.y];
SampledColorRun:
PROC [sMin, fMin:
INTEGER, fSize:
NAT] ~
TRUSTED {
start: Pair ← ImagerTransform.InverseTransform[[sMin, fMin], transform];
WHILE start.x >
NAT.
LAST
DO
start.x ← start.x - sampledColor.pa.xPixels;
ENDLOOP;
WHILE start.x < 0
DO
start.x ← start.x + sampledColor.pa.xPixels;
ENDLOOP;
WHILE start.y >
NAT.
LAST
DO
start.y ← start.y - sampledColor.pa.yPixels;
ENDLOOP;
WHILE start.y < 0
DO
start.y ← start.y + sampledColor.pa.yPixels;
ENDLOOP;
sampledColor.pa.get[sampledColor.pa, buffer, fSize, 0, Scaled.FromReal[start.x], Scaled.FromReal[start.y], xDelta, yDelta];
TRUSTED {
pm: PixelMap ← displayData[0];
lineBufferPtr: LONG POINTER ← pm.refRep.pointer + Basics.LongMult[(sMin - pm.sOrigin), pm.refRep.rast];
lineArray: LONG POINTER TO PACKED ARRAY [0..0) OF [0..256) ← lineBufferPtr;
IF invertOutput = 0
AND sampledColor.pa.maxSampleValue = 255
THEN {
FOR j:
CARDINAL
IN [0..fSize)
DO
lineArray[j+fMin] ← buffer[j];
ENDLOOP;
}
ELSE {
multiplier: CARDINAL ← (255*256+127)/sampledColor.pa.maxSampleValue;
FOR j:
CARDINAL
IN [0..fSize)
DO
lineArray[j+fMin] ← Basics.BITSHIFT[Basics.BITXOR[buffer[j]*multiplier, invertOutput], -8];
ENDLOOP;
};
};
};
SELECT sampledColor.colorOperator
FROM
$SampledBlack => invertOutput ← CARDINAL.LAST;
$Intensity => NULL;
ENDCASE => Imager.Error[$UnknownColorModel];
ImagerMasks.GenerateRuns[mask, displayData.compositeClipper, SampledColorRun, sTranslate, fTranslate];
};
ENDCASE => Imager.Error[$UnsupportedColorType];
};
bb: DeviceRectangle ← ImagerMasks.BoundingBox[mask];
bb.sMin ← bb.sMin + sTranslate;
bb.fMin ← bb.fMin + fTranslate;
IF color # displayData.cachedColor
THEN {
displayData.cachedColorData ←
NIL;
This is where we would calculate any cached color data.
displayData.cachedColor ← color;
};
DoUnderLock[displayData, LockedApplyMask, bb];
};
exampleImagerClass: ImagerPrivate.Class ~ ImagerDisplay.CreateImagerClass[exampleDisplayClass];
ImagerPrivate.RegisterDevice[exampleImagerClass];
END.