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: CARDINALIF 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.