Imager4bitDisplayImpl.mesa
This implements the low level routines for the color display at 4 bits per pixel.
Last Edited by:
GWilliams, July 29, 1983 5:18 pm
DIRECTORY
Real USING [FixC, Float],
Inline USING [LongMult, BITOR, BITSHIFT],
BitBlt USING [AlignedBBTable, BBTableSpace, BITBLT, BitBltTable, BitBltTablePtr],
Environment USING [wordsPerPage, bitsPerWord],
Space USING [Handle, Create, virtualMemory, defaultBase, LongPointer, Delete,
Map],
ImagerBasic USING [Transformation, TransformationRep, IntRectangle, IntPair,
CIEColor, CIEColorRep, PixelArray, PixelArrayRep],
ImagerDisplay USING [ByteSequence, ByteSequenceRep, Data4bitPixels, Data4bitPixelsRep, DisplayProcs, DisplayProcsRec,
ClipperRecord, VtxSequence],
ColorDisplay USING [Byte, GetMode, SetMode, TurnOn, GetColor, SetRed, GetPixel,
disconnected, width, height, baseA, wplA, SetColor],
Imager4bitDisplay; --exports to this interface
Imager4bitDisplayImpl: CEDAR MONITOR
IMPORTS Real, Inline, BitBlt, ColorDisplay, Space
EXPORTS Imager4bitDisplay
= BEGIN
OPEN ImagerBasic, ImagerDisplay, Inline;
NoDisplay: PUBLIC SIGNAL = CODE;
IncompatibleDisplayMode: PUBLIC SIGNAL = CODE;
IncompatiblePixelArray: PUBLIC SIGNAL = CODE;
BadBitAlignment: PUBLIC ERROR = CODE;
PixelWidth: TYPE = [0..maxPxlValue];
PixelIndex: TYPE = [0..pxpWd);
PixelAddr:
TYPE =
PACKED
ARRAY PixelIndex
OF PixelWidth;
bpPxl: CARDINAL = 4;
maxPxlValue: CARDINAL = 15;
bpWd: CARDINAL = Environment.bitsPerWord;
pxpWd: CARDINAL = bpWd/bpPxl;
maxDisplayWidth: CARDINAL = 4096; -- number in excess of maximum expected BITBLT width
storage: Space.Handle; -- space for double-buffering
swapUnitSize: CARDINAL ← 50; -- unbelieveable crap for getting large spaces
pixelBuffer: PixelArray ← NIL; -- ref for double buffering
bufferLimits: ClipperRecord;
nullBitBltTable: BitBlt.BitBltTable = [dst: [word: NIL, bit: 0], dstBpl: maxDisplayWidth,
src: [word: NIL, bit: 0], srcDesc: [srcBpl[maxDisplayWidth]],
width: 0, height: 1, flags: [disjoint: TRUE] ];
SQR: PROCEDURE [number: REAL] RETURNS [REAL] = INLINE { RETURN[number * number]; };
SGN:
PROCEDURE [number:
INTEGER]
RETURNS [
INTEGER] =
INLINE {
IF number >= 0 THEN RETURN[1] ELSE RETURN[-1]; };
GetBufferSpecs: PROCEDURE [place: IntPair] RETURNS [ CARDINAL, LONG POINTER ]
=
TRUSTED
INLINE {
wordsPerLine: CARDINAL;
pxlPtr: LONG POINTER;
IF pixelBuffer = NIL
THEN {
wordsPerLine ← ColorDisplay.wplA;
pxlPtr ← LOOPHOLE[ ColorDisplay.baseA +
Inline.LongMult[wordsPerLine, place.y] +
(place.x/pxpWd),
LONG POINTER];
}
ELSE {
wordsPerLine ←
IF pixelBuffer.xPixels
MOD pxpWd = 0
THEN pixelBuffer.xPixels/pxpWd
ELSE pixelBuffer.xPixels/pxpWd + 1 ;
pxlPtr ← LOOPHOLE[ NARROW[pixelBuffer.data, Data4bitPixels].p +
Inline.LongMult[wordsPerLine, place.y] +
(place.x/pxpWd),
LONG POINTER];
};
RETURN [ wordsPerLine, pxlPtr ];
};
GenColor:
PROC [n:
CARDINAL]
RETURNS[r,g,b: ColorDisplay.Byte] = {
SELECT n
FROM
0 => RETURN[1, 0, 0]; --red
1 => RETURN[0, 0, 1]; --blue
2 => RETURN[0, 1, 0]; --green
3 => RETURN[0, 1, 1]; --cyan
4 => RETURN[1, 0, 1]; --magenta
5 => RETURN [1, 1, 0]; --yellow
ENDCASE => RETURN[0,0,0];
};
SetUp:
PUBLIC
PROC []
RETURNS [Transformation,
REF ClipperRecord, DisplayProcs] =
TRUSTED {
get memory, pin it, set the transform and clipper
red, green, blue: ColorDisplay.Byte;
IF ColorDisplay.GetMode[] = ColorDisplay.disconnected
THEN
IF ColorDisplay.SetMode[ [
FALSE, 4, 0] ]
THEN ColorDisplay.TurnOn[]
ELSE ERROR NoDisplay[]
ELSE
IF ColorDisplay.GetMode[] # [
FALSE, 4, 0]
THEN ERROR IncompatibleDisplayMode[];
FOR i:
LONG
CARDINAL
IN [16..1024)
DO
[red, green, blue] ← GenColor[i MOD 6];
ColorDisplay.SetColor[ i, 0, red, green, blue] ENDLOOP; -- put in red, green, blue, cyan, magenta, yellow sequences
ColorDisplay.SetColor[ 5, 0, 168, 0, 87]; --trade with yellow(0) for debugging
ColorDisplay.SetColor[ 1, 0, 213, 0, 42];
ColorDisplay.SetColor[ 2, 0, 255, 0, 0]; -- full red
ColorDisplay.SetColor[ 3, 0, 210, 45, 0];
ColorDisplay.SetColor[ 4, 0, 168, 87, 0];
ColorDisplay.SetColor[ 0, 0, 126, 129, 0]; --yellow
ColorDisplay.SetColor[ 6, 0, 84, 171, 0];
ColorDisplay.SetColor[ 7, 0, 42, 213, 0];
ColorDisplay.SetColor[ 8, 0, 0, 255, 0]; --full green
ColorDisplay.SetColor[ 9, 0, 0, 213, 42];
ColorDisplay.SetColor[ 10, 0, 0, 171, 84];
ColorDisplay.SetColor[ 11, 0, 0, 129, 126];
ColorDisplay.SetColor[ 12, 0, 0, 87, 168];
ColorDisplay.SetColor[ 13, 0, 0, 45, 210];
ColorDisplay.SetColor[ 14, 0, 0, 0, 255]; --full blue
ColorDisplay.SetColor[ 15, 0, 128, 0, 127];
RETURN[
NEW [TransformationRep ← [1., 0., 0., 0.,-1., ColorDisplay.height, mirrorY] ],
NEW [ClipperRecord ← [1, ColorDisplay.width - 1, 1, ColorDisplay.height, rectangle, NIL] ],
NEW [DisplayProcsRec ← [ setUp: SetUp,
shutDown: ShutDown,
openPixelBuffer: OpenPixelBuffer,
closePixelBuffer: ClosePixelBuffer,
ColortoPixel: ColortoPixel,
pixeltoColor: PixeltoColor,
hilitePxls: HilitePxls,
movePxls: MovePxls,
storePxls: StorePxls,
loadPxls: LoadPxls,
storeScanSeg: StoreScanSeg,
loadScanSeg: LoadScanSeg,
loadTrapezoid: LoadTrapezoid,
setPixel: SetPxl,
getPixel: GetPxl,
drawLine: DrawLine,
tiler: Tiler
]
]
];
};
ShutDown:
PUBLIC PROC [] =
TRUSTED {
-- free memory in case the last user has left
IF NOT ColorDisplay.SetMode[ ColorDisplay.disconnected ] THEN ERROR NoDisplay[];
};
OpenPixelBuffer:
PUBLIC
PROC [box: ClipperRecord, buffer: PixelArray ←
NIL]
RETURNS [PixelArray] =
TRUSTED {
-- redirect writes for double-buffer
boxWidth: CARDINAL ← box.xMax - box.xMin + 1;
boxHeight: CARDINAL ← box.yMax - box.yMin + 1;
IF pixelBuffer # NIL THEN RETURN [pixelBuffer]; -- can't support multiple buffers yet
IF buffer = NIL
THEN {
wordsPerLine: CARDINAL ← IF boxWidth MOD pxpWd = 0 THEN boxWidth/pxpWd
ELSE boxWidth/pxpWd + 1;
pages: CARDINAL ← (Inline.LongMult[wordsPerLine, boxHeight + 1] /
Environment.wordsPerPage) + 1;
storage ← Space.Create[
pages,
Space.virtualMemory,
Space.defaultBase];
FOR offset:
CARDINAL ← 0, offset + swapUnitSize
WHILE offset + swapUnitSize <= pages DO -- get space in small pieces
[] ← Space.Create[size: swapUnitSize, parent: storage, base: offset]
ENDLOOP;
Space.Map[storage];
pixelBuffer ←
NEW[ PixelArrayRep ← [
xPixels: boxWidth,
yPixels: boxHeight,
maxSampleValue: maxPxlValue,
samplesPerPixel: 1,
data: NEW[ Data4bitPixelsRep ← [ Space.LongPointer[storage] ] ]
] ]
}
ELSE
IF
NOT ( buffer.maxSampleValue = maxPxlValue
AND
buffer.xPixels MOD pxpWd = 0 AND -- Check for integer row length in words
ISTYPE [buffer.data, Data4bitPixels] )
THEN SIGNAL IncompatibleDisplayMode[]
ELSE { buffer.xPixels ← boxWidth;
buffer.yPixels ← boxHeight;
pixelBuffer ← buffer; };
bufferLimits ← box; -- keep limits for translating loads to buffer
RETURN [pixelBuffer];
};
ClosePixelBuffer:
PUBLIC
PROC [] =
TRUSTED {
IF pixelBuffer = NIL THEN RETURN;
Space.Delete[storage]; -- deallocate bit storage
pixelBuffer ← NIL;
};
ColortoPixel:
PUBLIC PROC [color: CIEColor]
RETURNS [
LONG
CARDINAL] = {
-- Color to device transform
Finds color table entry with least sum-of-square difference from color
val: CIEColor;
sum, minSum: REAL;
entry: LONG CARDINAL;
FOR i:
LONG
CARDINAL
IN [0..maxPxlValue]
DO
val ← PixeltoColor[i];
sum ← SQR[val.x - color.x] + SQR[val.y - color.y] + SQR[val.Y - color.Y];
IF sum < minSum THEN { entry ← i; minSum ← sum; };
ENDLOOP;
RETURN [ entry ];
};
PixeltoColor:
PUBLIC PROC [pxlValue:
LONG
CARDINAL]
RETURNS [CIEColor] =
TRUSTED {
r, g, b: [0..maxPxlValue]; -- RGB from color display lookup table
rr, rg, rb: REAL; -- Imager RGB value (range 0.-1.)
color: CIEColor ← NEW[ CIEColorRep ];
[r, g, b] ← ColorDisplay.GetColor[ pxlValue ];
rr ← r/Real.Float[maxPxlValue];
rg ← g/Real.Float[maxPxlValue];
rb ← b/Real.Float[maxPxlValue];
color.x ← Real.FixC[rr * LAST[CARDINAL]]; -- this should be proceeded by a transform
color.y ← Real.FixC[rg * LAST[CARDINAL]];
color.Y ← Real.FixC[rb * LAST[CARDINAL]];
RETURN [ color ];
};
HilitePxls:
PUBLIC PROC [area: IntRectangle] = {
-- Device dependent highlighting scheme
};
MovePxls:
PUBLIC PROC [source: IntRectangle, destination: IntPair] =
TRUSTED {
-- move on display
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];
srcPxlPtr, dstPxlPtr: LONG POINTER;
wordsPerLine: CARDINAL;
[wordsPerLine, dstPxlPtr] ← GetBufferSpecs[destination];
[wordsPerLine, srcPxlPtr] ← GetBufferSpecs[ [source.x, source.y] ];
bb^ ← nullBitBltTable;
bb.dst.word ← dstPxlPtr;
bb.dst.bit ← (destination.x MOD pxpWd) * bpPxl;
bb.src.word ← srcPxlPtr;
bb.src.bit ← (source.x MOD pxpWd) * bpPxl;
bb.srcDesc.srcBpl ← bb.dstBpl ← wordsPerLine * bpWd;
bb.width ← MIN[source.w * bpPxl, (ColorDisplay.width - destination.x) * bpPxl];
bb.height ← MIN[source.h, ColorDisplay.height - destination.y];
BitBlt.BITBLT[bb];
};
StorePxls:
PUBLIC PROC [source: IntRectangle]
RETURNS [PixelArray] = {
dest: PixelArray ← NEW[ PixelArrayRep ];
RETURN [dest];
};
LoadPxls:
PUBLIC
PROC [source: PixelArray ←
NIL, destination: IntPair, transparent:
BOOLEAN] =
TRUSTED {
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];
IF source = NIL
THEN {
IF pixelBuffer =
NIL
THEN
RETURN[]
-- error return if no buffer
ELSE source ← pixelBuffer; } -- default to double buffer
ELSE IF NOT ( source.maxSampleValue = maxPxlValue AND
source.xPixels MOD pxpWd = 0 AND -- Check for integer row length in words
ISTYPE [source.data, Data4bitPixels] )
THEN ERROR IncompatiblePixelArray[]; -- error return if not BLT-able
bb^ ← nullBitBltTable;
bb.dst.word ← LOOPHOLE[ ColorDisplay.baseA +
Inline.LongMult[ColorDisplay.wplA, destination.y] +
(destination.x/pxpWd),
LONG POINTER];
IF destination.x MOD pxpWd # 0 THEN bb.dst.bit ← bpPxl;
bb.src.word ← NARROW[source.data, Data4bitPixels].p;
bb.srcDesc.srcBpl ←
IF source # pixelBuffer
THEN source.xPixels * bpPxl
ELSE IF pixelBuffer.xPixels MOD pxpWd = 0
THEN (pixelBuffer.xPixels/pxpWd)*bpWd
ELSE (pixelBuffer.xPixels/pxpWd + 1)*bpWd;
bb.dstBpl ← ColorDisplay.wplA * bpWd;
bb.width ← MIN[source.xPixels * bpPxl, (ColorDisplay.width - 1 - destination.x) * bpPxl];
bb.height ← MIN[source.yPixels, ColorDisplay.height - destination.y];
BitBlt.BITBLT[bb];
};
StoreScanSeg:
PUBLIC PROC [x, y, length:
CARDINAL]
RETURNS [ByteSequence] = {
segment: ByteSequence ← NEW [ ByteSequenceRep[length/pxpWd] ];
RETURN [segment];
};
LoadScanSeg:
PUBLIC
PROC [x, y, length:
CARDINAL, segment: ByteSequence, transparent:
BOOLEAN] =
TRUSTED {
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];
pxlPtr: LONG POINTER;
wordsPerLine: CARDINAL;
IF pixelBuffer # NIL THEN { y ← y - bufferLimits.yMin; x ← x - bufferLimits.xMin; };
[wordsPerLine, pxlPtr] ← GetBufferSpecs[ [x, y] ];
bb^ ← nullBitBltTable;
bb.dst.word ← pxlPtr;
bb.dst.bit ← (x MOD pxpWd) * bpPxl;
bb.src.word ← LOOPHOLE[segment, LONG POINTER];
bb.width ← length * bpPxl;
BitBlt.BITBLT[bb];
};
LoadTrapezoid:
PUBLIC
PROC [top, bottom, leftTop, leftBot, rightTop, rightBot:
CARDINAL, pxlValue:
LONG
CARDINAL] =
TRUSTED {
-- Scan convert trapezoid, constant color
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];
pxlPtr: LONG POINTER;
color: CARDINAL;
wordsPerLine: CARDINAL;
shortPxlValue: CARDINAL ← pxlValue;
IF pixelBuffer #
NIL
THEN {
-- adjust for buffer limits
top ← top - bufferLimits.yMin; bottom ← bottom - bufferLimits.yMin;
leftTop ← leftTop - bufferLimits.xMin; leftBot ← leftBot - bufferLimits.xMin;
rightTop ← rightTop - bufferLimits.xMin; rightBot ← rightBot - bufferLimits.xMin;
};
[wordsPerLine, pxlPtr] ← GetBufferSpecs[ [leftTop, bottom] ];
IF leftTop = leftBot
AND rightTop = rightBot
THEN {
-- rectangle
color ← BITOR[BITSHIFT[shortPxlValue, 4], shortPxlValue]; -- make pxlValue brick for BitBlt
color ← BITOR[BITSHIFT[color, 8], color];
bb^ ← nullBitBltTable;
bb.flags.gray ← TRUE;
bb.dst.word ← pxlPtr;
bb.dst.bit ← (leftBot MOD bpPxl) * bpPxl;
bb.src.word ← @color;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 15, heightMinusOne: 0];
bb.dstBpl ← wordsPerLine * bpWd;
bb.width ← MIN[(rightBot - leftBot + 1) * bpPxl, (ColorDisplay.width - leftBot) * bpPxl];
bb.height ← MIN[(top - bottom + 1), ColorDisplay.height - bottom];
BitBlt.BITBLT[bb];}
ELSE {
-- not rectangle
};
};
SetPxl:
PUBLIC PROC [x, y:
CARDINAL, pxlValue:
LONG
CARDINAL] =
TRUSTED {
byte: [0..256) ← pxlValue;
ColorDisplay.SetRed[ x, y, byte ];
};
GetPxl:
PUBLIC PROC [x, y:
CARDINAL]
RETURNS [
LONG
CARDINAL] =
TRUSTED {
pixel: LONG CARDINAL;
r, g, b: [0..256);
[r, g, b] ← ColorDisplay.GetPixel[x, y];
pixel ← r;
RETURN [ pixel ];
};
DrawLine:
PUBLIC PROC [a, b: IntPair, pxlValue:
LONG
CARDINAL] =
TRUSTED {
-- fast line, constant color
Using a slightly modified Bresenham's algorithm
byteValue: [0..maxPxlValue] ← pxlValue;
increment, bias, error, wordsPerLine, yBump: INTEGER; t: IntPair;
pxlPtr: LONG POINTER TO PixelAddr;
whichByte: INTEGER;
IF pixelBuffer #
NIL
THEN {
-- adjust for buffer limits
a.x ← a.x - bufferLimits.xMin; a.y ← a.y - bufferLimits.yMin;
b.x ← b.x - bufferLimits.xMin; b.y ← b.y - bufferLimits.yMin;
};
IF a.x > b.x THEN { t ← a; a ← b; b ← t; }; -- make sure of positive-going x
[wordsPerLine, pxlPtr] ← GetBufferSpecs[a];
IF (b.x - a.x) > ABS[b.y - a.y]
THEN {
-- more horizontal line
increment ← 2 * (b.y - a.y);
bias ← 2 * (b.x - a.x);
yBump ← SGN[increment] * wordsPerLine;
increment ← ABS[increment]; bias ← ABS[bias];
error ← increment - bias/2;
whichByte ← a.x MOD pxpWd; --Get to proper pixel in the word
FOR i:
CARDINAL
IN [0..
ABS[b.x-a.x]]
DO
pxlPtr[whichByte] ← byteValue;
IF (whichByte ← (whichByte + 1))
>= pxpWd
THEN {pxlPtr ← pxlPtr + 1; whichByte ← 0};
IF error > 0
THEN { pxlPtr ← pxlPtr + yBump; error ← error - bias; };
error ← error + increment;
ENDLOOP;
}
ELSE {
-- more vertical line
increment ← 2 * (b.x - a.x);
bias ← 2 * (b.y - a.y);
yBump ← SGN[bias] * wordsPerLine;
increment ← ABS[increment]; bias ← ABS[bias];
error ← increment - bias/2;
whichByte ← a.x MOD pxpWd; --Get to proper pixel in the word
FOR i:
CARDINAL
IN [0..
ABS[b.y-a.y]]
DO
pxlPtr[whichByte] ← byteValue;
pxlPtr ← pxlPtr + yBump;
IF error > 0
THEN {
error ← error - bias;
IF (whichByte ← (whichByte + 1))
>= pxpWd
THEN {pxlPtr ← pxlPtr + 1; whichByte ← 0};
};
error ← error + increment;
ENDLOOP;
};
};
Tiler:
PUBLIC PROC [VtxCount:
CARDINAL, Vertices: VtxSequence] = {
};
END.