ImagerPixelMapsExtrasImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Frank Crow, August 16, 1984 9:16:11 am PDT
Fancy operations on two-dimensional arrays of pixels.
Last Edited by: Stone, August 15, 1984 7:05:30 pm PDT
DIRECTORY
Basics       USING [LongMult, BITSHIFT, BITAND, logBitsPerWord, bitsPerWord,
             HighHalf, LongNumber, BITOR, BITNOT, BytePair],
ImagerBasic      USING [IntPair],
PrincOps      USING [DstFunc, SrcFunc, BitAddress, BBTableSpace, BBptr],
PrincOpsUtils    USING [AlignedBBTable, BITBLT],
ImagerPixelMaps    USING [PixelMap, PixelMapRep, Function, DeviceRectangle,
           BoundedWindow],
ImagerPixelMapsExtras  USING [SetBit, Set2Bits, Set4Bits, Set8Bits, Set16Bits, ByteSequence];
ImagerPixelMapsExtrasImpl: CEDAR PROGRAM
IMPORTS Basics, ImagerPixelMaps, PrincOpsUtils, ImagerPixelMapsExtras
EXPORTS ImagerPixelMapsExtras
~ BEGIN
Type Definitions
BytePair: TYPE ~ Basics.BytePair;
IntPair: TYPE ~ ImagerBasic.IntPair;
DeviceRectangle: TYPE ~ ImagerPixelMaps.DeviceRectangle;
Function: TYPE ~ ImagerPixelMaps.Function;
ByteSequence: TYPE ~ ImagerPixelMapsExtras.ByteSequence;
PixelMap: TYPE ~ ImagerPixelMaps.PixelMap;
RECORD [
These parameters define a window within the buffer, as well as where the buffer is positioned in device space; they are fair game for clients to modify.
sOrigin, fOrigin: INTEGER,
sMin, fMin: INTEGER,
sSize, fSize: NAT,
refRep: REF PixelMapRep
];
PixelMapRep: TYPE ~ RECORD [
ref: REF, pointer: LONG POINTER, words: LONG CARDINAL,
lgBitsPerPixel: [0..4], -- logarithm base two of bitsPerPixel
rast: CARDINAL, lines: CARDINAL
Stored contiguously, with each line padded to a word boundary, rast words per line. The ref is for garbage collection hacks; the long pointer points to the actual bits. Must have rast*lines <= words. Clients should try to avoid dealing directly with this record.
];
Utility Procedures
lgBitsPerWord: NAT ~ Basics.logBitsPerWord;
bitsPerWord: NAT ~ Basics.bitsPerWord;
BitAddr: UNSAFE PROC [lineStart: LONG POINTER, pixel: CARDINAL, lgBitsPerPixel: INTEGER] RETURNS [PrincOps.BitAddress] ~ UNCHECKED {
RETURN [[
word: lineStart + Basics.BITSHIFT[pixel, lgBitsPerPixel-lgBitsPerWord],
bit: Basics.BITAND[Basics.BITSHIFT[pixel, lgBitsPerPixel], bitsPerWord-1]
]]
};
SGN: PROCEDURE [number: INTEGER] RETURNS [INTEGER] = INLINE {
IF number >= 0 THEN RETURN[1] ELSE RETURN[-1];
};
Pixel operations
replicator: ARRAY [0..4] OF CARDINAL ~ [0FFFFH, 05555H, 01111H, 00101H, 00001H];
FillConstantTrap: PUBLIC PROC [destination: PixelMap,  -- trapezoid, constant color
     top, bottom, leftTop, leftBot, rightTop, rightBot: NAT,
     pxlValue: CARDINAL, function: Function ← [null, null]] ~ TRUSTED {
lgBitsPerPixel: NAT ~ destination.refRep.lgBitsPerPixel;
bitsPerPixel: NAT ~ Basics.BITSHIFT[1, lgBitsPerPixel];
value: CARDINAL ~ MIN[pxlValue, Basics.BITSHIFT[1, bitsPerPixel] - 1];
rastWds: CARDINAL ~ destination.refRep.rast;
rastBits: CARDINAL ~ rastWds * bitsPerWord;
EdgeBlock: TYPE = RECORD [xPos: LONG CARDINAL, xIncr: LONG INTEGER];
MakeEdge: UNSAFE PROC[xTop, xBot, height: NAT] RETURNS [edge: EdgeBlock] = {
LOOPHOLE[edge.xPos, Basics.LongNumber].highbits ← xBot;
LOOPHOLE[edge.xPos, Basics.LongNumber].lowbits ← 0;
LOOPHOLE[edge.xIncr, Basics.LongNumber].highbits ← xTop - xBot;
LOOPHOLE[edge.xIncr, Basics.LongNumber].lowbits ← 0;
IF height > 1 THEN edge.xIncr ← edge.xIncr / height;
};
DoScanSeg: UNSAFE PROC[] ~ { PrincOpsUtils.BITBLT[bb]; };
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ← PrincOpsUtils.AlignedBBTable[@bbspace];
replicatedPixel: CARDINAL ← Basics.BITAND[value, Basics.BITSHIFT[1, Basics.BITSHIFT[1, lgBitsPerPixel]]-1] * replicator[lgBitsPerPixel]; -- make brick for BitBlt
lEdge: EdgeBlock ← MakeEdge[leftTop, leftBot, top - bottom];
rEdge: EdgeBlock ← MakeEdge[rightTop, rightBot, top - bottom];
bb^ ← [
dst: BitAddr[destination.refRep.pointer + Basics.LongMult[bottom, rastWds],
    leftBot, lgBitsPerPixel],
dstBpl: rastBits,
src: [word: @replicatedPixel, bit: 0],
srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]],
height: 1,
width: Basics.BITSHIFT[rightTop - leftTop, lgBitsPerPixel],
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE, srcFunc: function.srcFunc, dstFunc: function.dstFunc]
];
FOR height: NAT IN [bottom..top] DO
bb.dst ← BitAddr[destination.refRep.pointer + Basics.LongMult[height, rastWds],
      Basics.HighHalf[lEdge.xPos], lgBitsPerPixel];
bb.width ← Basics.BITSHIFT[ Basics.HighHalf[rEdge.xPos] - Basics.HighHalf[lEdge.xPos] + 1,
         lgBitsPerPixel ];
DoScanSeg[];
lEdge.xPos ← lEdge.xPos + lEdge.xIncr;
rEdge.xPos ← rEdge.xPos + rEdge.xIncr;
ENDLOOP;
};
FillSmoothTrap: PUBLIC PROC [destination: PixelMap,  -- bilinear interpolated 8-bit color
        top, bottom, leftTop, leftBot, rightTop, rightBot: NAT,
        leftTopVal, leftBotVal, rightTopVal, rightBotVal: NAT] ~ TRUSTED {
rastWds: CARDINAL ~ destination.refRep.rast;
rastBits: CARDINAL ~ rastWds * bitsPerWord;
EdgeBlock: TYPE = RECORD [xPos, value: LONG CARDINAL, xIncr, valIncr: INT];
MakeEdge: UNSAFE PROC[xTop, valTop, xBot, valBot, height: NAT]
     RETURNS [edge: EdgeBlock] = {
valBot ← MIN[valBot, 255]; valTop ← MIN[valTop, 255];
LOOPHOLE[edge.xPos, Basics.LongNumber].highbits ← xBot;
LOOPHOLE[edge.xPos, Basics.LongNumber].lowbits ← 0;
LOOPHOLE[edge.xIncr, Basics.LongNumber].highbits ← xTop - xBot;
LOOPHOLE[edge.xIncr, Basics.LongNumber].lowbits ← 0;
LOOPHOLE[edge.value, Basics.LongNumber].highbits ← valBot;
LOOPHOLE[edge.value, Basics.LongNumber].lowbits ← 0;
LOOPHOLE[edge.valIncr, Basics.LongNumber].highbits ← valTop - valBot;
LOOPHOLE[edge.valIncr, Basics.LongNumber].lowbits ← 0;
IF height > 1
THEN { edge.xIncr ← edge.xIncr / height; edge.valIncr ← edge.valIncr / height; };
};
DoScanSeg: PROC[lx, lVal, rx, rVal: LONG CARDINAL] = TRUSTED {
value, valIncr: INT;
xStart: NAT ← Basics.HighHalf[lx];
xFinish: NAT ← Basics.HighHalf[rx];
length: INTEGER ← xFinish - xStart;
leftByte: BOOLEAN;
valIncr ← INT[rVal] - INT[lVal];
IF ABS[length] >= 1 THEN valIncr ← valIncr / length;
IF xFinish >= xStart
THEN {
value ← lVal; pxlPtr ← scanPtr + LOOPHOLE[Basics.HighHalf[lx] / 2, CARDINAL];
}
ELSE {            -- twisted or otherwise backwards
xTemp: CARDINAL ← xStart; xStart ← xFinish; xFinish ← xTemp;
value ← rVal; pxlPtr ← scanPtr + LOOPHOLE[Basics.HighHalf[rx] / 2, CARDINAL];
};
leftByte ← (xStart MOD 2) = 0;
FOR i: NAT IN [xStart..xFinish] DO
IF leftByte
THEN pxlPtr^.high ← Basics.HighHalf[value]
ELSE { pxlPtr^.low ← Basics.HighHalf[value]; pxlPtr ← pxlPtr + 1; };
leftByte ← NOT leftByte;
value ← value + valIncr;
ENDLOOP;
};
scanPtr, pxlPtr: LONG POINTER TO Basics.BytePair;
wordsPerLine: NAT ← destination.refRep.rast;
lEdge: EdgeBlock ← MakeEdge[leftTop, leftTopVal, leftBot, leftBotVal, top - bottom];
rEdge: EdgeBlock ← MakeEdge[rightTop, rightTopVal, rightBot, rightBotVal, top - bottom];
IF destination.refRep.lgBitsPerPixel # 3 THEN ERROR;
TRUSTED { scanPtr ← LOOPHOLE[
     destination.refRep.pointer + Basics.LongMult[destination.refRep.rast, bottom],
    LONG POINTER TO Basics.BytePair]; };
FOR height: NAT IN [bottom..top] DO
DoScanSeg[lEdge.xPos, lEdge.value, rEdge.xPos, rEdge.value];
lEdge.xPos ← lEdge.xPos + lEdge.xIncr; lEdge.value ← lEdge.value + lEdge.valIncr;
rEdge.xPos ← rEdge.xPos + rEdge.xIncr; rEdge.value ← rEdge.value + rEdge.valIncr;
scanPtr ← scanPtr + wordsPerLine;
ENDLOOP;
};
DrawLine: PUBLIC PROC [destination: PixelMap, p1, p2: IntPair, -- fast line, constant color
        pxlValue: NAT, function: Function ← [null, null]] ~ {
increment, bias, error, sBump, t, shiftDist: INTEGER;
wrdPtr: LONG POINTER TO WORD;
p1s, p1f, p2s, p2f: INTEGER;
Get necessary constants based on bits per pixel
lgBitsPerPixel: NAT ~ destination.refRep.lgBitsPerPixel;
logPxlsPerWd: NAT ~ Basics.logBitsPerWord - lgBitsPerPixel;
bitsPerPixel: NAT ~ Basics.BITSHIFT[1, lgBitsPerPixel];
maxShift: NAT ~ Basics.bitsPerWord - bitsPerPixel;
maxValue: NAT ~ Basics.BITSHIFT[1, bitsPerPixel] - 1;
value: NAT ~ MIN[pxlValue, maxValue];
p1f ← p1.x; p1s ← p1.y; p2f ← p2.x; p2s ← p2.y;
Make sure of positive-going fast coordinate
IF p1f > p2f THEN { t ← p1f; p1f ← p2f; p2f ← t; t ← p1s; p1s ← p2s; p2s ← t; }; 
Get pointer to initial word and bit offset
TRUSTED {
wrdPtr ← destination.refRep.pointer + Basics.LongMult[destination.refRep.rast, p1s]
          + Basics.BITSHIFT[p1f, -logPxlsPerWd];
};
shiftDist ← maxShift - Basics.BITSHIFT[
Basics.BITAND[ p1f, Basics.BITSHIFT[1, logPxlsPerWd] - 1], -- p1f MOD pixelsPerWord
lgBitsPerPixel
];
IF (p2f - p1f) > ABS[p2s - p1s]
More horizontal line (moves faster along fast axis)
THEN {
increment ← 2 * (p2s - p1s);
bias ← 2 * (p2f - p1f);
sBump ← SGN[increment] * destination.refRep.rast;
increment ← ABS[increment];
error ← increment - bias/2;
IF lgBitsPerPixel = 3    -- speedup for 8 bits per pixel
THEN FOR i: NAT IN [0..(p2f-p1f)] DO TRUSTED {
IF shiftDist = 0
THEN { LOOPHOLE[wrdPtr^, BytePair].low ← value;
   wrdPtr ← wrdPtr + 1; shiftDist ← 8; }
ELSE { LOOPHOLE[wrdPtr^, BytePair].high ← value; shiftDist ← 0; };
IF error > 0 THEN TRUSTED { wrdPtr ← wrdPtr + sBump; error ← error - bias; };
error ← error + increment;
};
ENDLOOP
ELSE FOR i: NAT IN [0..(p2f-p1f)] DO
TRUSTED {
wrdPtr^ ← Basics.BITOR[      -- deposit pixel bits in word
Basics.BITAND[wrdPtr^, Basics.BITNOT[Basics.BITSHIFT[maxValue, shiftDist]]],
Basics.BITSHIFT[value, shiftDist]
];
};
IF shiftDist = 0 THEN TRUSTED { wrdPtr ← wrdPtr + 1; shiftDist ← maxShift; }
     ELSE shiftDist ← shiftDist - bitsPerPixel;
IF error > 0 THEN TRUSTED { wrdPtr ← wrdPtr + sBump; error ← error - bias; };
error ← error + increment;
ENDLOOP;  
}
More vertical line (moves faster along slow axis)
ELSE {
j: NAT ← Basics.BITSHIFT[shiftDist, -lgBitsPerPixel];
pixelsPerWd: NAT ~ Basics.BITSHIFT[1, logPxlsPerWd];
mask, values: ARRAY [0..16) OF CARDINAL;
FOR i: NAT IN [0..pixelsPerWd) DO
mask[i] ← Basics.BITNOT[Basics.BITSHIFT[maxValue, Basics.BITSHIFT[i, lgBitsPerPixel]]];
values[i] ← Basics.BITSHIFT[value, Basics.BITSHIFT[i, lgBitsPerPixel]];
ENDLOOP;
increment ← 2 * (p2f - p1f);
bias ← 2 * (p2s - p1s);
sBump ← SGN[bias] * destination.refRep.rast;
bias ← ABS[bias];
error ← increment - bias/2;
IF lgBitsPerPixel = 3    -- speedup for 8 bits per pixel
THEN FOR i: NAT IN [0..ABS[p2s - p1s]] DO TRUSTED {
IF shiftDist = 0
THEN { LOOPHOLE[wrdPtr^, BytePair].low ← value;
   IF error > 0
   THEN { wrdPtr ← wrdPtr + 1; shiftDist ← 8; error ← error - bias; };
 }
ELSE { LOOPHOLE[wrdPtr^, BytePair].high ← value;
   IF error > 0
   THEN { shiftDist ← 0; error ← error - bias; };
 };
wrdPtr ← wrdPtr + sBump;
error ← error + increment;
};
ENDLOOP
ELSE FOR i: NAT IN [0..ABS[p2s - p1s]] DO
TRUSTED {
wrdPtr^ ← Basics.BITOR[ Basics.BITAND[wrdPtr^, mask[j]] , values[j]];
};
TRUSTED { wrdPtr ← wrdPtr + sBump; };
IF error > 0
THEN {
error ← error - bias;
IF j = 0 THEN TRUSTED { wrdPtr ← wrdPtr + 1; j ← pixelsPerWd - 1; }
     ELSE j ← j - 1;
};
error ← error + increment;
ENDLOOP;
};
};
DrawBltLine: PUBLIC PROC [destination: PixelMap, -- fast line using BitBLT, constant color
       p1, p2: IntPair,
       pxlValue: NAT, function: Function ← [null, null]] ~ TRUSTED {
increment, bias, error, sBump, t: INTEGER;
p1s, p1f, p2s, p2f: INTEGER;
wrdPtr: LONG POINTER;
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ← PrincOpsUtils.AlignedBBTable[@bbTableSpace];
Get necessary constants based on bits per pixel
lgBitsPerPixel: NAT ~ destination.refRep.lgBitsPerPixel;
logBitsPerPixel: NAT ~ lgBitsPerPixel;
bitsPerPixel: NAT ~ Basics.BITSHIFT[1, lgBitsPerPixel];
logPxlsPerWd: NAT ~ Basics.logBitsPerWord - logBitsPerPixel;
pxlsPerWordLessOne: NAT ~ Basics.BITSHIFT[1, logPxlsPerWd] - 1;
replicatedPixel: CARDINAL ← Basics.BITAND[
        value,
        Basics.BITSHIFT[1, Basics.BITSHIFT[1, logBitsPerPixel]] - 1 ]
       * replicator[logBitsPerPixel]; -- make brick for BitBlt
pxlCnt, leftOverPixels: NAT ← 0;        --pixels since last jag in line
value: NAT ~ MIN[pxlValue, Basics.BITSHIFT[1, bitsPerPixel] - 1];
p1f ← p1.x; p1s ← p1.y; p2f ← p2.x; p2s ← p2.y;
Make sure of positive-going fast coordinate
IF p1f > p2f THEN { t ← p1f; p1f ← p2f; p2f ← t; t ← p1s; p1s ← p2s; p2s ← t; }; 
Get pointer to initial word and bit offset
wrdPtr ← destination.refRep.pointer + Basics.LongMult[destination.refRep.rast, p1s]
           + Basics.BITSHIFT[p1f, -logPxlsPerWd];
leftOverPixels ← Basics.BITAND[p1f, pxlsPerWordLessOne];
Set up BITBLT table
bb^ ← [     
dst: [word: wrdPtr, bit: Basics.BITSHIFT[leftOverPixels, logBitsPerPixel]],
dstBpl: destination.refRep.rast * Basics.bitsPerWord,
src: [word: @replicatedPixel, bit: 0],
srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]],
height: 1,
width: bitsPerPixel,
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE,
  srcFunc: function.srcFunc, dstFunc: function.dstFunc]
];
IF (p2f - p1f) > ABS[p2s - p1s]
More horizontal line (moves faster along fast axis)
THEN {
increment ← 2 * (p2s - p1s);
bias ← 2 * (p2f - p1f);
sBump ← SGN[increment] * destination.refRep.rast;
increment ← ABS[increment];
error ← increment - bias/2;
FOR i: NAT IN [0..(p2f-p1f)] DO
pxlCnt ← pxlCnt + 1;
IF error > 0 THEN { -- end of straight segment
bb.dst ← [word: wrdPtr,
  bit: Basics.BITSHIFT[leftOverPixels, logBitsPerPixel]
  ];
bb.width ← Basics.BITSHIFT[pxlCnt, lgBitsPerPixel];
PrincOpsUtils.BITBLT[bb];
pxlCnt ← pxlCnt + leftOverPixels;
wrdPtr ← wrdPtr + sBump + Basics.BITSHIFT[pxlCnt, -logPxlsPerWd];
leftOverPixels ← Basics.BITAND[pxlCnt, pxlsPerWordLessOne];
pxlCnt ← 0;
error ← error - bias
};
error ← error + increment;
ENDLOOP;
bb.dst ← [word: wrdPtr, -- BLT last segment
  bit: Basics.BITSHIFT[leftOverPixels, logBitsPerPixel]
  ];
bb.width ← Basics.BITSHIFT[pxlCnt, logBitsPerPixel];
PrincOpsUtils.BITBLT[bb];
}
More vertical line (moves faster along slow axis)
ELSE {
lastWrdPtr: LONG POINTER TO CARDINAL ← wrdPtr;
increment ← 2 * (p2f - p1f);
bias ← 2 * (p2s - p1s);
sBump ← SGN[bias] * destination.refRep.rast;
bias ← ABS[bias];
error ← increment - bias/2;
FOR i: NAT IN [0..ABS[p2s - p1s]] DO
pxlCnt ← pxlCnt + 1;
wrdPtr ← wrdPtr + sBump;
IF error > 0 THEN {
bb.dst ← [ word: IF sBump < 0 THEN wrdPtr ELSE lastWrdPtr,
   bit: Basics.BITSHIFT[leftOverPixels, logBitsPerPixel]
  ];
bb.height ← pxlCnt;
PrincOpsUtils.BITBLT[bb];
IF leftOverPixels = pxlsPerWordLessOne
THEN { wrdPtr ← wrdPtr + 1; leftOverPixels ← 0; }
ELSE leftOverPixels ← leftOverPixels + 1;
lastWrdPtr ← wrdPtr;
pxlCnt ← 0;
error ← error - bias;
};
error ← error + increment;
ENDLOOP;
bb.dst ← [word: IF sBump < 0 THEN wrdPtr ELSE lastWrdPtr, -- BLT last segment
  bit: Basics.BITSHIFT[leftOverPixels, logBitsPerPixel]
  ];
bb.height ← pxlCnt;
PrincOpsUtils.BITBLT[bb];
};
};
LoadScanSeg: PUBLIC PROC[destination: PixelMap,
         s, f: INTEGER, length: NAT,
         segment: LONG POINTER, offset: NAT ← 0] = TRUSTED{
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ← PrincOpsUtils.AlignedBBTable[@bbspace];
pxpWd: NAT ~ Basics.BITSHIFT[1, Basics.logBitsPerWord - destination.refRep.lgBitsPerPixel];
bb^ ← [
dst: [
word: LOOPHOLE[ destination.refRep.pointer
     + Basics.LongMult[destination.refRep.rast, s] + (f / pxpWd),
    LONG POINTER],
bit: Basics.BITSHIFT[f MOD pxpWd, destination.refRep.lgBitsPerPixel]
],
dstBpl: 0,
src: [
word: segment + offset/pxpWd,
bit: Basics.BITSHIFT[offset MOD pxpWd, destination.refRep.lgBitsPerPixel]
],
srcDesc: [srcBpl[0]],
width: Basics.BITSHIFT[length , destination.refRep.lgBitsPerPixel],
height: 1,
flags: [disjoint: TRUE]
];
PrincOpsUtils.BITBLT[bb];
};
StoreScanSeg: PUBLIC PROC [source: PixelMap,
         s, f: INTEGER, length: NAT,
         segment: LONG POINTER, offset: NAT ← 0] = TRUSTED{
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ← PrincOpsUtils.AlignedBBTable[@bbspace];
pxpWd: NAT ~ Basics.BITSHIFT[1, Basics.logBitsPerWord - source.refRep.lgBitsPerPixel];
bb^ ← [
dst: [
word: segment + offset/pxpWd,
bit: Basics.BITSHIFT[offset MOD pxpWd, source.refRep.lgBitsPerPixel]
],
dstBpl: 0,
src: [
word: LOOPHOLE[ source.refRep.pointer
     + Basics.LongMult[source.refRep.rast, s] + (f / pxpWd),
     LONG POINTER],
bit: Basics.BITSHIFT[f MOD pxpWd, source.refRep.lgBitsPerPixel]
],
srcDesc: [srcBpl[0]],
width: Basics.BITSHIFT[length , source.refRep.lgBitsPerPixel],
height: 1,
flags: [disjoint: TRUE]
];
PrincOpsUtils.BITBLT[bb];
};
SetPixel: PUBLIC PROC [destination: PixelMap, s, f: INTEGER, value: NAT] ~ {
Raises bounds fault if the point is not in the window.
bounds: DeviceRectangle ← ImagerPixelMaps.BoundedWindow[destination];
sCheck: NAT ← bounds.sSize-1-NAT[s-bounds.sMin];
fCheck: NAT ← bounds.fSize-1-NAT[f-bounds.fMin];
SELECT destination.refRep.lgBitsPerPixel FROM
0 => ImagerPixelMapsExtras.SetBit[destination, s, f, value];
1 => ImagerPixelMapsExtras.Set2Bits[destination, s, f, value];
2 => ImagerPixelMapsExtras.Set4Bits[destination, s, f, value];
3 => ImagerPixelMapsExtras.Set8Bits[destination, s, f, value];
4 => ImagerPixelMapsExtras.Set16Bits[destination, s, f, value];
ENDCASE => ERROR;
};
END.