PixelsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Frank Crow, June 5, 1986 1:22:13 pm PDT
Fancy operations on two-dimensional arrays of pixels.
DIRECTORY
Process     USING [ SecondsToTicks, Pause ],
Basics      USING [BITOR, BYTE],
Atom      USING [PutPropOnList, GetPropFromList, PropList],
Real      USING [FixI, Float],
Terminal     USING [Virtual, FrameBuffer, GetColorFrameBufferA,
         GetColorFrameBufferB, GetBWFrameBuffer, Current,
         ModifyColorFrame],
QuickViewer    USING [QuickView],
ViewerClasses   USING [Viewer],
ViewerOps    USING [OpenIcon],
Imager     USING [Context, Transformation, Rectangle],
ImagerBackdoor   USING [GetBounds, GetT],
ImagerRasterPrivate  USING [Data],
ImagerPixelMap   USING [DeviceRectangle],
ImagerDitheredDevice USING [ContextFromSampleMap],
ImagerMappedDevice USING [ContextFromSampleMap],
SampleMapOps   USING [Create, SampleMap, SubMap, Transfer, GetSample, PutSample,
         Fill, FromVM, FromSubMap, Get, Put, Function,
         Buffer, ObtainBuffer, SampleMapRep],
ImagerColor    USING [RGB],
Pixels      USING [SubMapSequence, SampleSet, SubMap, SampleSetSequence,
         PixelBuffer, Extent, ErrorDesc];
PixelsImpl: CEDAR PROGRAM
IMPORTS Basics, Atom, ImagerBackdoor, SampleMapOps, Real, Terminal, ViewerOps, Process
EXPORTS Pixels
~ BEGIN OPEN Pixels;
Type Definitions
BYTE: TYPE ~ Basics.BYTE;
RGB: TYPE ~ ImagerColor.RGB;
SampleSet: TYPE ~ Pixels.SampleSet;
SampleSetSequence: TYPE ~ Pixels.SampleSetSequence;
SubMapSequence: TYPE ~ Pixels.SubMapSequence;
PixelBuffer: TYPE ~ Pixels.PixelBuffer;
Extent: TYPE ~ Pixels.Extent;
PixelsError: PUBLIC SIGNAL [reason: Pixels.ErrorDesc] = CODE;
SubMap: TYPE ~ Pixels.SubMap;
RECORD [
subMap: RECORD[sampleMap: SampleMap, start: CVEC ← [0,0], size: CVEC ← lastCVEC] ],
df: NAT ← 1
];
SampleMap: TYPE ~ SampleMapOps.SampleMap;
REF SampleMapRep: TYPE ~ RECORD [
It is unsafe for clients to alter these fields.
sSize: NAT, -- number of scan lines
fSize: NAT, -- number of samples per scan line
bitsPerSample: [0..bitsPerWord], -- number of bits per sample
base: PrincOps.BitAddress, -- starting bit address
bitsPerLine: NAT, -- bits per scan line
ref: REF -- for garbage collection
];
Utility Procedures
XfmMapPlace: PUBLIC PROC [ map: SubMap, x, y: INTEGER ]
      RETURNS [newX, newY: INTEGER] ~ {
x ← map.df * (x + map.subMap.start.f);
y ← MAX[0, INTEGER[map.subMap.size.s]-1 - y + map.subMap.start.s];
RETURN [ newX: x, newY: y ];
};
ByteAvrgWgtd: PUBLIC PROC[ b1, b2, wgt: BYTE ] RETURNS[ bOut: BYTE ] ~ {
bOut ← ( CARDINAL[b1]*256 + CARDINAL[wgt]*CARDINAL[b2 - b1] ) / 256;
bOut ← Basics.BITOR[ bOut, 1 ];   -- Von Neumann rounding
};
SumLessProd: PUBLIC PROC[ b1, b2: BYTE ] RETURNS[ bOut: BYTE ] ~ {
bOut ← ( CARDINAL[b1]*256 + CARDINAL[b2]*256 - CARDINAL[b2]*CARDINAL[b1] ) / 256;
bOut ← Basics.BITOR[ bOut, 1 ];   -- Von Neumann rounding
};
GetSampleSet: PUBLIC PROC[size: NAT] RETURNS[SampleSet] ~ {
RETURN[ SampleMapOps.ObtainBuffer[size] ];
};
SampleMapFromFrameBuffer: PROC [frameBuffer: Terminal.FrameBuffer] RETURNS [SampleMap] ~ {
RETURN [ SampleMapOps.FromVM[
fSize:    frameBuffer.width,
sSize:    frameBuffer.height,
bitsPerSample: frameBuffer.bitsPerPixel,
vm:    frameBuffer.vm
] ];
};
PositionFromProps: PROCEDURE [props: Atom.PropList] RETURNS [NAT] = {
position: REF NATNARROW[ Atom.GetPropFromList[props, $Alpha] ];
IF position = NIL
THEN ERROR PixelsError[[$MisMatch, "Expected alpha buffer"]]
ELSE RETURN[position^];
};
Swap: PROCEDURE [first, second: INTEGER] RETURNS [INTEGER, INTEGER] = {
RETURN [second, first];
};
SGN: PROCEDURE [number: INTEGER] RETURNS [INTEGER] = INLINE {
IF number >= 0 THEN RETURN[1] ELSE RETURN[-1];
};
Creation Operations
Create: PUBLIC PROC [width, height: NAT, pixelSizes: SampleSet]
  RETURNS
[ PixelBuffer ] ~ {
Allocates bits and builds PixelBuffer record.
buf: PixelBuffer;
IF pixelSizes = NIL THEN SIGNAL PixelsError[[$Mismatch, "Pixel sizes needed"]];
buf.pixels ← NEW[SubMapSequence[pixelSizes.length] ];
FOR i: NAT IN [0..pixelSizes.length) DO
IF pixelSizes[i] > 0 THEN buf.pixels[i].subMap.sampleMap ← SampleMapOps.Create[
fSize:    width,
sSize:    height,
bitsPerSample:  pixelSizes[i]
];
buf.pixels[i].df ← 1;
buf.pixels[i].subMap.start ← [0, 0];
buf.pixels[i].subMap.size ← [f: width, s: height];
ENDLOOP;
buf.height ← height;
buf.width ← width;
buf.samplesPerPixel ← pixelSizes.length;
RETURN [buf];
};
GetFromImagerContext: PUBLIC PROC [context: Imager.Context, alpha, depth: BOOLEAN]
        RETURNS[PixelBuffer, Imager.Rectangle] ~ {
Forces pixel buffer onto Imager context in those circumstances where it is possible
buf: PixelBuffer;
bounds: Extent;
noColor: BOOLEANFALSE;
newBds: Imager.Rectangle;
xfm: Imager.Transformation;
vt: Terminal.Virtual ← Terminal.Current[];
numMaps: NAT ← 0;
WITH context.data SELECT FROM
quickView: QuickViewer.QuickView => {
viewer: ViewerClasses.Viewer ← quickView.viewer;
WHILE viewer.parent # NIL DO viewer ← viewer.parent ENDLOOP;
IF viewer.column # color THEN noColor ← TRUE;
IF viewer.iconic THEN ViewerOps.OpenIcon[ viewer ];
};
imagerRaster: ImagerRasterPrivate.Data => {
viewToDevice: Imager.Transformation ← imagerRaster.viewToDevice;
viewClipBox: ImagerPixelMap.DeviceRectangle ← imagerRaster.viewClipBox;
SELECT imagerRaster.device.class.type FROM
$Bitmap => noColor ← TRUE;
$GrayDisplay, $DitheredColorDisplay, $FullColorDisplay => noColor ← FALSE;
ENDCASE  => PixelsError[[$Unimplemented, "Unknown device type"]];
};
ENDCASE  => PixelsError[[$Unimplemented, "Unknown Imager.Context.data type"]];
Process.Pause[ Process.SecondsToTicks[2] ];  -- pause for viewers to settle
IF noColor
THEN buf ← GetFromLF[vt, context]
ELSE buf ← GetFromTerminal[vt, alpha, depth];
xfm ← ImagerBackdoor.GetT[context];
IF NOT xfm.integerTrans
THEN SIGNAL PixelsError[[$Unimplemented, "Only translations allowed"]];
newBds ← ImagerBackdoor.GetBounds[context];
newBds.x ← newBds.x + xfm.tx; newBds.y ← newBds.y + xfm.ty;
IF NOT noColor THEN {
IF newBds.y < 29 THEN { newBds.y ← 0; newBds.h ← buf.height; }; -- ooh! yuk, horrible!!
IF newBds.x < 5 THEN { newBds.x ← 0; newBds.w ← buf.width; };    -- junk!!
NewBds needs to be the Imagers device bounds, not the client bounds!!!
bounds.x ← Real.FixI[ newBds.x ];
bounds.y ← MAX[ Real.FixI[ newBds.y ], 27 ]; -- 27 = viewer banner & menu, yuk!!!
bounds.w ← MIN[buf.width - bounds.x, Real.FixI[newBds.w]];
bounds.h ← MIN[buf.height - bounds.y, Real.FixI[newBds.h]];
newBds ← [Real.Float[bounds.x],
   Real.Float[buf.height - (bounds.y + bounds.h)],   -- right-side-up bounds
   Real.Float[bounds.w], Real.Float[bounds.h] ];
FOR i: NAT IN [0..buf.samplesPerPixel) DO
buf.pixels[i].subMap.start ← [f: bounds.x, s: bounds.y];
buf.pixels[i].subMap.size ← [f: bounds.w, s: bounds.h];
ENDLOOP;
};
buf.props ← Atom.PutPropOnList[ buf.props, $ImagerContext, context ];  -- store context
buf.props ← Atom.PutPropOnList[ buf.props, $VirtualTerminal, vt ];   -- store terminal
IF noColor THEN buf.props ← Atom.PutPropOnList[ buf.props, $RenderMode, $LF ];
RETURN [buf, newBds];
};
GetFromTerminal: PUBLIC PROC [vt: Terminal.Virtual, alpha, depth: BOOLEAN]
      RETURNS[PixelBuffer] ~ {
Makes pixel buffer out of terminal color display, if there is one
fbA: Terminal.FrameBuffer ← vt.GetColorFrameBufferA[];
fbB: Terminal.FrameBuffer ← vt.GetColorFrameBufferB[];
map: NAT ← 0;
buf: PixelBuffer;
colorDepth: NAT ← 0;
IF fbA = NIL THEN SIGNAL PixelsError[[$Mismatch, "No color display"]];
IF fbA # NIL
THEN { map ← map + 1; IF fbA.bitsPerPixel > 8 THEN map ← map + 1; };
IF fbB # NIL THEN map ← map + 1;
colorDepth ← map;
IF alpha THEN map ← map + 1;
IF depth THEN map ← map + 1;
buf.pixels ← NEW[ SubMapSequence[map] ];
map ← 0;
IF fbA.bitsPerPixel > 8
THEN {   -- rg interleaved buffer, replicate sample map offset by one byte
map ← map + 1;            -- green map first ( low bytes )
buf.pixels[map].subMap.sampleMap ← SampleMapFromFrameBuffer[fbA];
buf.pixels[map].subMap.sampleMap.bitsPerSample ← 8; -- adjust to byte width samples
buf.pixels[map].subMap.sampleMap.fSize ← buf.pixels[map].subMap.sampleMap.fSize * 2;
buf.pixels[map-1] ← buf.pixels[map];      -- copy to get red map
buf.pixels[map].subMap.sampleMap ← SampleMapOps.FromSubMap[ [
sampleMap: buf.pixels[map].subMap.sampleMap,
start: [f: 1, s: 0]         -- offset by one to get lower bytes
] ];
buf.pixels[map-1].df ← buf.pixels[map].df ← 2;
buf.pixels[map-1].subMap.start ← buf.pixels[map].subMap.start ← [0, 0];
buf.pixels[map-1].subMap.size
      ← buf.pixels[map].subMap.size ← [f: fbA.width*2, s: fbA.height];
}
ELSE {
buf.pixels[map].subMap.sampleMap ← SampleMapFromFrameBuffer[fbA];
buf.pixels[map].df ← 1;
buf.pixels[map].subMap.start ← [0, 0];
buf.pixels[map].subMap.size ← [f: fbA.width, s: fbA.height];
};
map ← map + 1;
IF fbB # NIL THEN {
buf.pixels[map].subMap.sampleMap ← SampleMapFromFrameBuffer[fbB];
buf.pixels[map].df ← 1;
buf.pixels[map].subMap.start ← [0, 0];
buf.pixels[map].subMap.size ← [f: fbB.width, s: fbB.height];
map ← map + 1;
};
IF alpha THEN {
buf.pixels[colorDepth].subMap.sampleMap ← SampleMapOps.Create[
fSize:    fbA.width,
sSize:    fbA.height,
bitsPerSample:  8
];
buf.pixels[colorDepth].df ← 1;
buf.pixels[colorDepth].subMap.start ← [0, 0];
buf.pixels[colorDepth].subMap.size ← [f: fbA.width, s: fbA.height];
buf.props ← Atom.PutPropOnList[ buf.props, $Alpha, NEW[NAT ← colorDepth] ];
map ← map + 1;
};
IF depth THEN {
position: NATIF alpha THEN colorDepth+1 ELSE colorDepth;
buf.pixels[position].subMap.sampleMap ← SampleMapOps.Create[
fSize:    fbA.width,
sSize:    fbA.height,
bitsPerSample:  16
];
buf.pixels[position].df ← 1;
buf.pixels[position].subMap.start ← [0, 0];
buf.pixels[position].subMap.size ← [f: fbA.width, s: fbA.height];
buf.props ← Atom.PutPropOnList[ buf.props, $Depth, NEW[NAT ← position] ];
map ← map + 1;
};
buf.samplesPerPixel ← map;
buf.height ← fbA.height;
buf.width ← fbA.width;
buf.props ← Atom.PutPropOnList[ buf.props, $VirtualTerminal, vt ];   -- store terminal
RETURN [buf];
};
GetFromLF: PROC [vt: Terminal.Virtual, context: Imager.Context] RETURNS[PixelBuffer] ~ {
box: ImagerPixelMap.DeviceRectangle;
fb: Terminal.FrameBuffer ← vt.GetBWFrameBuffer[];
buf: PixelBuffer;
WITH context.data SELECT FROM
quickView: QuickViewer.QuickView => {
viewer: ViewerClasses.Viewer ← quickView.viewer;
WHILE viewer.parent # NIL DO viewer ← viewer.parent ENDLOOP;
box ← [
fMin: quickView.outer.wx + 2,
sMin: fb.height - quickView.outer.wy - 2 - quickView.viewer.ch,
fSize: quickView.viewer.cw,
sSize: quickView.viewer.ch
];
};
imagerRaster: ImagerRasterPrivate.Data => {
box ← imagerRaster.viewClipBox
};
ENDCASE  => PixelsError[[$Unimplemented, "Unknown Imager.Context.data type"]];
buf.pixels ← NEW[ SubMapSequence[1] ];
buf.pixels[0].subMap.sampleMap ← SampleMapFromFrameBuffer[fb];
buf.pixels[0].df ← 1;
buf.pixels[0].subMap.start ← [
f: box.fMin,
s: box.sMin
];
buf.pixels[0].subMap.size ← [f: box.fSize, s: box.sSize];
buf.samplesPerPixel ← 1;
buf.height ← box.sSize;
buf.width ← box.fSize;
buf.props ← Atom.PutPropOnList[ buf.props, $VirtualTerminal, vt ];   -- store terminal
RETURN [buf];
};
PixelBuffer Operations
TerminalFromBuffer: PUBLIC PROC [buf: PixelBuffer] RETURNS[vt: Terminal.Virtual] ~ {
RETURN[ NARROW[Atom.GetPropFromList[buf.props, $VirtualTerminal], Terminal.Virtual] ];
};
ImagerContextFromBuffer: PUBLIC PROC [buf: PixelBuffer, type: ATOMNIL]
         RETURNS
[ctx: Imager.Context] ~ {
Allows imager calls to act on your pixels  Not yet implemented properly!!
Convert sample maps to pixel map
ImagerSmooth.Create: PROC [pixelMap: PixelMap, component: ATOM,
viewToPixel: Transformation ← NIL, initialScale: REAL ← 1.0,
change: PROC[changeData: REF, d: DeviceRectangle, action: PROC] ← NIL,
changeData: REFNIL, cacheFonts: BOOLTRUE, surfaceUnitsPerPixel: NAT ← 5]
RETURNS
[Context];
ImagerRaster.NewBitmapDevice[frame: ImagerPixelMap.PixelMap, pixelsPerInch: REAL ← 72]
RETURNS [Device];
ImagerRaster.NewGrayDevice[terminal: Terminal.Virtual] RETURNS [Device];
ImagerRaster.NewColorMapDevice[terminal: Terminal.Virtual, bpp: NAT ← 8] RETURNS [Device];
ImagerRaster.NewColor24Device[terminal: Terminal.Virtual] RETURNS [Device];
ImagerRaster.Create[device: Device, pixelUnits: BOOLFALSE, fontCache: ImagerCache.Ref ← NIL, rastWeight: REAL ← 1.0, fontTuner: FontTuner ← NIL, class: REF Imager.ClassRep ← NIL
] RETURNS [Context];
ref: REF ANY ← Atom.GetPropFromList[buf.props, $ImagerContext];
IF ref # NIL
THEN ctx ← NARROW[ref, Imager.Context]
ELSE {
kind: ATOM;
mapList: LIST OF SampleMap ← NIL;
ctx ← NIL;
IF type # NIL
THEN kind ← type
ELSE SELECT buf.samplesPerPixel FROM
1, 2  => kind ← $Mapped;
ENDCASE => kind ← $Color24;
SELECT kind FROM
$Gray   => SIGNAL PixelsError[[$UnImplemented, "Grey display unsupported"]];
$Dithered => ctx ← ImagerDitheredDevice.ContextFromSampleMap[
frame: buf.pixels[0],
displayHeight: buf.height,
pixelUnits: TRUE
];
$Mapped  => ctx ← ImagerMappedDevice.ContextFromSampleMap[
frame: buf.pixels[0],
displayHeight: buf.height,
pixelUnits: TRUE
];
$Color24  => SIGNAL PixelsError[[$UnImplemented, "no 24-bit color"]];
ENDCASE  => SIGNAL PixelsError[[$UnImplemented, "Unknown device type"]];
FOR i: NAT DECREASING IN [0..buf.pixels.length) DO
mapList ← CONS[buf.pixels[i], mapList];
ENDLOOP;
ImagerOps.ContextFromSampleMaps[mapList, NIL, kind]; -- unimplemented!!
};
RETURN[ctx];
};
Fill: PUBLIC PROC [buf: PixelBuffer, pixel: SampleSet] ~ {
Clear pixels to specified pixel value
DoIt: PROC[] ~ {
Write[buf, [0, 0, buf.width, buf.height], pixel];
};
vt : Terminal.Virtual ← TerminalFromBuffer[buf];
IF pixel = NIL THEN {
pixel ← GetSampleSet[buf.pixels.length];
FOR i: NAT IN [0..pixel.length) DO pixel[i] ← 0; ENDLOOP;
};
IF vt # NIL
THEN {
n: NAT ← buf.samplesPerPixel - 1;
Terminal.ModifyColorFrame[vt, DoIt,
buf.pixels[n].subMap.start.f,
buf.pixels[n].subMap.start.s,
buf.pixels[n].subMap.start.f + buf.pixels[0].subMap.size.f,
buf.pixels[n].subMap.start.s + buf.pixels[0].subMap.size.s
];
}
ELSE DoIt[];
};
Transfer: PUBLIC PROC [dstBuf, srcBuf: PixelBuffer] ~ {
numMaps: NATMIN[ dstBuf.samplesPerPixel, srcBuf.samplesPerPixel+1 ];
DoIt: PROC[] ~ {
FOR i: NAT IN [0 .. numMaps) DO
heightDif: NATMAX[ 0,
INTEGER[dstBuf.pixels[i].subMap.size.s]
- INTEGER[srcBuf.pixels[i].subMap.size.s]
];
IF dstBuf.pixels[i].df = 1  -- write only the first of a set of interleaved maps
OR dstBuf.pixels[i].subMap.sampleMap.base.bit = 0
THEN SampleMapOps.Transfer[
dest: dstBuf.pixels[i].subMap.sampleMap,
destStart: [
f: dstBuf.pixels[i].subMap.start.f * dstBuf.pixels[i].df,
s: dstBuf.pixels[i].subMap.start.s + heightDif
],
source: srcBuf.pixels[i].subMap
];
ENDLOOP;
};
vt : Terminal.Virtual ← TerminalFromBuffer[dstBuf];
IF vt # NIL
THEN {
n: NAT ← dstBuf.samplesPerPixel - 1;
Terminal.ModifyColorFrame[vt, DoIt,
dstBuf.pixels[n].subMap.start.f,
dstBuf.pixels[n].subMap.start.s,
dstBuf.pixels[n].subMap.start.f + dstBuf.pixels[0].subMap.size.f,
dstBuf.pixels[n].subMap.start.s + dstBuf.pixels[0].subMap.size.s
];
}
ELSE DoIt[];
};
Copy: PUBLIC PROC [ destination, source: PixelBuffer, destArea, srcArea: Extent,
      op: ATOM ← $Write ] ~ {
DoIt: PROC[] ~ {
func: SampleMapOps.Function;
SELECT op FROM
$AND  => func ← [ and, null];
$OR  => func ← [ or, null];
$XOR  => func ← [ xor, null];
ENDCASE => func ← [null, null];
IF destination.pixels = NIL OR source.pixels = NIL
THEN SIGNAL PixelsError[[$Mismatch, "Buffer is nil"]];
SELECT op FROM
$AND, $OR, $XOR, $Write  => {
 Ensure that result fills the lower left corner of the destination area
srcArea.w ← MIN[ destArea.w, srcArea.w]; srcArea.h ← MIN[ destArea.h, srcArea.h];
FOR i: NAT IN [0..source.pixels.length) DO
srcX, srcY, dstX, dstY: NAT;
[srcX, srcY] ← XfmMapPlace[source.pixels[i],
        srcArea.x, srcArea.y + srcArea.h - 1];
[dstX, dstY] ← XfmMapPlace[destination.pixels[i],
         destArea.x, destArea.y + srcArea.h - 1];
IF destination.pixels[i].df = 1  -- copy only one from interleaved set
OR destination.pixels[i].subMap.sampleMap.base.bit = 0
THEN SampleMapOps.Transfer[
dest: destination.pixels[i].subMap.sampleMap,
destStart: [f: dstX, s: dstY],
source: [
sampleMap: source.pixels[i].subMap.sampleMap,
start: [f: srcX, s: srcY],
size: [f: srcArea.w*source.pixels[i].df, s: srcArea.h]
],
function: func
];
ENDLOOP;
};
$WriteOver, $WriteUnder  => {
srcSeg: REF SampleSetSequence ← NIL;
FOR j: NAT IN [0 .. srcArea.h) DO
srcSeg ← GetScanSeg[source, srcArea.x, j + srcArea.y, srcArea.w, srcSeg];
PutScanSeg[destination, destArea.x, j + destArea.y, destArea.w, srcSeg, op];
ENDLOOP;
};
ENDCASE => SIGNAL PixelsError[[$UnImplemented, "Unknown operation"]];
};
vt : Terminal.Virtual ← TerminalFromBuffer[destination];
IF vt # NIL
THEN Terminal.ModifyColorFrame[vt, DoIt, destArea.x, destArea.y,
             destArea.x+destArea.w, destArea.y+destArea.h]
ELSE DoIt[];
};
ShowOnImagerContext: PUBLIC PROC [context: Imager.Context, buf: PixelBuffer] ~ {
Puts pixels from displayMemory onto supplied context using Imager calls
SIGNAL PixelsError[[$Unimplemented, "Hasn't been written yet"]];
};
Pixel operations
GetPixel: PUBLIC PROC [buf: PixelBuffer, x, y: NAT, pixel: SampleSet ← NIL]
   RETURNS[ SampleSet ] ~ {
IF pixel = NIL OR pixel.maxLength < buf.samplesPerPixel
THEN pixel ← GetSampleSet[buf.samplesPerPixel];
FOR i: NAT IN [0..buf.pixels.length) DO
nx, ny: NAT;
[nx, ny] ← XfmMapPlace[buf.pixels[i], x, y];
pixel[i] ← SampleMapOps.GetSample[ buf.pixels[i].subMap.sampleMap, [f: nx, s: ny] ]
ENDLOOP;
pixel.length ← buf.pixels.length;   -- make sure length field is up to date
RETURN [pixel];
};
PutPixel: PUBLIC PROC [ buf: PixelBuffer, x, y: NAT, pixel: SampleSet ] ~ {
IF pixel = NIL OR pixel.length # buf.samplesPerPixel
THEN SIGNAL PixelsError[[$Mismatch, "Pixel and buffer don't match"]];
FOR i: NAT IN [0..buf.pixels.length) DO
nx, ny: NAT;
[nx, ny] ← XfmMapPlace[buf.pixels[i], x, y];
SampleMapOps.PutSample[ buf.pixels[i].subMap.sampleMap, [f: nx, s: ny], pixel[i] ]
ENDLOOP;
};
GetScanSeg: PUBLIC PROC [ buf: PixelBuffer, x, y, length: NAT,
         pixels: REF SampleSetSequence ← NIL ]
    RETURNS[ REF SampleSetSequence ] ~ {
IF pixels = NIL OR pixels.length # buf.samplesPerPixel
THEN pixels ← NEW[ SampleSetSequence[buf.samplesPerPixel] ];
FOR i: NAT IN [0..buf.samplesPerPixel) DO
nx, ny: NAT;
IF pixels[i] = NIL OR pixels[i].length < length THEN pixels[i] ← GetSampleSet[length];
[nx, ny] ← XfmMapPlace[buf.pixels[i], x, y];
SampleMapOps.Get[
buffer: pixels[i],
count: pixels[i].length,
sampleMap: buf.pixels[i].subMap.sampleMap,
f: nx,
s: ny,
df: buf.pixels[i].df
];
ENDLOOP;
RETURN[pixels];
};
PutScanSeg: PUBLIC PROC [ buf: PixelBuffer, x, y, length: NAT,
         pixels: REF SampleSetSequence, op: ATOM ← $Write ] ~ {
DoIt: PROC[] ~ {
func: SampleMapOps.Function;
SELECT op FROM
$AND  => func ← [ and, null];
$OR  => func ← [ or, null];
$XOR  => func ← [ xor, null];
ENDCASE => func ← [null, null];
IF pixels = NIL OR pixels.length # buf.samplesPerPixel
THEN SIGNAL PixelsError[[$Mismatch, "Pixel and buffer don't match"]];
SELECT op FROM
$AND, $OR, $XOR, $Write  => {
FOR i: NAT IN [0..buf.samplesPerPixel) DO
nx, ny: NAT;
IF pixels[i] = NIL OR pixels[i].length < length
THEN SIGNAL PixelsError[[$Mismatch, "Buffer has too few samples per pixel"]];
[nx, ny] ← XfmMapPlace[buf.pixels[i], x, y];
SampleMapOps.Put[
buffer: pixels[i],
count: length,
sampleMap: buf.pixels[i].subMap.sampleMap,
f: nx,
s: ny,
df: buf.pixels[i].df,
function: func
];
ENDLOOP;
};
$WriteOver, $WriteUnder  => {
alpha: NAT ← PositionFromProps[buf.props];
dstSeg: REF SampleSetSequence ← GetScanSeg[buf, x, y, length];
FOR j: NAT IN [0 .. length) DO
IF op = $WriteOver THEN {
dstSeg[alpha][j] ← SumLessProd[ dstSeg[alpha][j], pixels[alpha][j] ];
FOR k: NAT IN [0..buf.samplesPerPixel) DO IF k # alpha THEN
dstSeg[k][j] ← ByteAvrgWgtd[ b1: dstSeg[k][j], b2: pixels[k][j],
         wgt: pixels[alpha][j] ];
ENDLOOP;
}
ELSE IF op = $WriteUnder THEN {
cvrge: NATMIN[ pixels[alpha][j], 255 - dstSeg[alpha][j] ];
dstSeg[alpha][j] ← dstSeg[alpha][j] + cvrge;
FOR k: NAT IN [0..buf.samplesPerPixel) DO IF k # alpha THEN
dstSeg[k][j] ← dstSeg[k][j] + CARDINAL[cvrge * pixels[k][j]] / 256;
ENDLOOP;
};
ENDLOOP;
PutScanSeg[buf, x, y, length, dstSeg];
};
ENDCASE => SIGNAL PixelsError[[$UnImplemented, "Unknown operation"]];
};
vt : Terminal.Virtual ← TerminalFromBuffer[buf];
IF vt # NIL
THEN Terminal.ModifyColorFrame[vt, DoIt, x, y, x+length, y+1]
ELSE DoIt[];
};
dstPxl: SampleSet ← GetSampleSet[4];
srcPxl: SampleSet ← GetSampleSet[4]; -- for read-mod-write ops
Write: PROC[ buf: PixelBuffer, area: Extent, pixel: SampleSet,
      func: SampleMapOps.Function ← [null, null] ] ~ {
FOR i: NAT IN [0..buf.samplesPerPixel) DO
x, y, w, h: NAT;
[x, y] ← XfmMapPlace[ buf.pixels[i], area.x, area.y + area.h - 1 ]; -- upper left corner
w ← MIN[ buf.pixels[i].subMap.sampleMap.fSize - x, area.w];  -- clip to map limits
h ← MIN[ buf.pixels[i].subMap.sampleMap.sSize - y, area.h];
IF buf.pixels[i].df = 2          -- kludge for Dorado 24-bit color
THEN IF buf.pixels[i].subMap.sampleMap.base.bit = 0
THEN {
value: CARDINAL ← pixel[i] * 256 + pixel[i+1];
sampleMap: SampleMapOps.SampleMap ← NEW[SampleMapOps.SampleMapRep];
sampleMap^ ← buf.pixels[i].subMap.sampleMap^;
sampleMap.bitsPerSample ← 16; sampleMap.fSize ← sampleMap.fSize / 2;
x ← x / buf.pixels[i].df; -- correct for pixel size change
SampleMapOps.Fill[ [sampleMap, [f: x, s: y], [f: w, s: h] ], value, func ];
}
ELSE {}
ELSE SampleMapOps.Fill[        -- case for continuous pixels
dest: [ buf.pixels[i].subMap.sampleMap, [f: x, s: y], [f: w, s: h] ],
value: pixel[i],
function: func
];
ENDLOOP;
};
WriteOver: PROC[ buf: PixelBuffer, area: Extent, pixel: SampleSet, alpha: NAT] ~ {
scanSeg: REF SampleSetSequence ← NIL;
FOR j: NAT IN [area.y..area.y+area.h) DO
scanSeg ← GetScanSeg[buf, area.x, j, area.w, scanSeg];
FOR i: NAT IN [area.x..area.x+area.w) DO
sum alphas minus overlap estimate
scanSeg[alpha][i] ← SumLessProd[ scanSeg[alpha][i], pixel[alpha] ];
FOR k: NAT IN [0..pixel.length) DO IF k # alpha THEN
scanSeg[k][i] ← ByteAvrgWgtd[ b1: scanSeg[k][i], b2: pixel[k], wgt: pixel[alpha] ];
ENDLOOP;
ENDLOOP;
PutScanSeg[buf, area.x, j, area.w, scanSeg];
ENDLOOP;
};
WriteUnder: PROC[ buf: PixelBuffer, area: Extent, pixel: SampleSet, alpha: NAT] ~ {
scanSeg: REF SampleSetSequence ← NIL;
FOR j: NAT IN [area.y..area.y+area.h) DO
scanSeg ← GetScanSeg[buf, area.x, j, area.w, scanSeg];
FOR i: NAT IN [0..area.w) DO
IF scanSeg[alpha][i] = 0
THEN {          -- untouched pixel
scanSeg[alpha][i] ← pixel[alpha];
FOR k: NAT IN [0..pixel.length) DO IF k # alpha THEN
scanSeg[k][i] ← CARDINAL[pixel[alpha] * pixel[k]] / 255; -- alpha*new
ENDLOOP;
}
ELSE IF scanSeg[alpha][i] < 255 THEN {  -- used but not completely covered pixel
cvrge: NATMIN[ pixel[alpha], 255 - scanSeg[alpha][i] ]; -- < unused old alpha
scanSeg[alpha][i] ← scanSeg[alpha][i] + cvrge;  -- sum alphas (always < 256)
FOR k: NAT IN [0..pixel.length) DO IF k # alpha THEN
scanSeg[k][i] ← scanSeg[k][i] + CARDINAL[cvrge * pixel[k]] / 255;
ENDLOOP;              -- old + alpha*new
};
ENDLOOP;
PutScanSeg[buf, area.x, j, area.w, scanSeg];
ENDLOOP;
};
PixelOp: PUBLIC PROC [ buf: PixelBuffer, area: Extent, pixel: SampleSet,
       op: ATOM ← $Write ] ~ {
DoIt: PROC[] ~ {
func: SampleMapOps.Function;
SELECT op FROM
$AND  => func ← [ and, null];
$OR  => func ← [ or, null];
$XOR  => func ← [ xor, null];
ENDCASE => func ← [null, null];
IF pixel = NIL OR pixel.length < buf.samplesPerPixel
THEN SIGNAL PixelsError[[$Mismatch, "Pixel and buffer don't match"]];
SELECT op FROM
$AND, $OR, $XOR, $Write  => Write[buf, area, pixel, func];
$WriteOver       => {
alpha: NAT ← PositionFromProps[buf.props];
SELECT pixel[alpha] FROM -- pixel[alpha] is coverage (alpha)
255  => Write[buf, area, pixel, func];  -- replacement if alpha saturated
0   => {};           -- no effect if alpha is zero
ENDCASE => WriteOver[buf, area, pixel, alpha];  -- blend otherwise
};
$WriteUnder      => {
alpha: NAT ← PositionFromProps[buf.props];
IF pixel[alpha] > 0 THEN WriteUnder[buf, area, pixel, alpha]; -- no-op if alpha zero
}; 
ENDCASE => SIGNAL PixelsError[[$UnImplemented, "Unknown operation"]];
};
vt : Terminal.Virtual ← TerminalFromBuffer[buf];
IF vt # NIL
THEN {
xOffset: NAT ← buf.pixels[buf.samplesPerPixel-1].subMap.start.f;
yOffset: NAT ← buf.pixels[buf.samplesPerPixel-1].subMap.start.s;
Terminal.ModifyColorFrame[vt, DoIt, area.x + xOffset, area.y + yOffset,
           area.x+area.w + xOffset, area.y+area.h + yOffset];
}
ELSE DoIt[];
};
Visible value operations
GetValue: PUBLIC PROC [buf: PixelBuffer, x, y: NAT, map: NAT ← 0]
   RETURNS
[ value: CARDINAL ] ~ {
[x, y] ← XfmMapPlace[buf.pixels[map], x, y];
value ← SampleMapOps.GetSample[ buf.pixels[map].subMap.sampleMap, [f: x, s: y] ];
};
PutValue: PUBLIC PROC [ buf: PixelBuffer, x, y: NAT, value: CARDINAL, map: NAT ← 0 ] ~ {
[x, y] ← XfmMapPlace[buf.pixels[map], x, y];
SampleMapOps.PutSample[ buf.pixels[map].subMap.sampleMap, [f: x, s: y] , value ];
};
ValueOp: PUBLIC PROC [ buf: PixelBuffer, area: Extent, value: CARDINAL, map: NAT ← 0,
        op: ATOM ← $Write ] ~ {
func: SampleMapOps.Function;
SELECT op FROM
$AND  => func ← [ and, null];
$OR  => func ← [ or, null];
$XOR  => func ← [ xor, null];
ENDCASE => func ← [null, null];
[area.x, area.y] ← XfmMapPlace[ buf.pixels[map], area.x, area.y + area.h - 1 ];
IF buf.pixels[map].df > 1 THEN area.x ← area.x / buf.pixels[map].df;
SELECT op FROM
$AND, $OR, $XOR, $Write  =>
IF buf.pixels[map].df > 1
THEN {        -- kludge for Dorado 24-bit color
scanSeg: SampleSet ← GetSampleSet[area.w];
FOR x: NAT IN [0 .. area.w) DO scanSeg[x] ← value; ENDLOOP;
FOR y: NAT IN [area.y.. area.y + area.h) DO
SampleMapOps.Put[
buffer: scanSeg,
count: area.w,
sampleMap: buf.pixels[map].subMap.sampleMap,
f: area.x,
s: y,
df: buf.pixels[map].df,
function: func
];
ENDLOOP;
}
ELSE SampleMapOps.Fill[
dest: [buf.pixels[map].subMap.sampleMap, [f: area.x, s: area.y],[f: area.w, s: area.h]],
value: value,
function: func
];
ENDCASE => SIGNAL PixelsError[[$UnImplemented, "Operation not for single value"]];
};
END.