SelectRegionsImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Provides facilities for selecting rectangular regions on the screen.
Created Thursday, February 14, 1985 1:13:25 am PST
Last edited by Eric Nickell, January 7, 1986 8:13:34 pm PST
DIRECTORY
SelectRegions,
Basics USING [RawWords],
RefTab USING [Create, Fetch, Ref, Store],
SampleMapOps USING [Buffer, Clear, Create, Fill, Function, Get, GetPointer, ObtainBuffer, Put, PutSample, SampleMap, Transfer, UnsafeCreate];
SelectRegionsImpl: CEDAR PROGRAM
IMPORTS RefTab, SampleMapOps
EXPORTS SelectRegions
= BEGIN
OPEN SelectRegions;
Buffer: TYPE ~ SampleMapOps.Buffer;
SampleArrayHack: TYPE ~ REF SampleArrayHackRep;
SampleArrayHackRep: TYPE ~ RECORD [
SEQUENCE n: NAT OF SampleVectorHack
];
SampleVectorHack: TYPE ~ LONG POINTER TO PACKED ARRAY [0..0) OF [0..256);
ArrayHack: TYPE ~ REF ArrayHackRep;
ArrayHackRep: TYPE ~ RECORD [
SEQUENCE n: NAT OF VectorHack
];
VectorHack: TYPE ~ LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN;
sampleMapToSampleArrayHack: RefTab.Ref ~ RefTab.Create[];
Twiddling procedures
maskFcns: ARRAY Operation OF SampleMapOps.Function ← [
on: [or, null],
off: [and, complement],
flip: [xor, null]
];
PaintBrush: PUBLIC PROC [sm: SampleMap, loc: CVEC, op: Operation, brush: SampleMap] ~ {
SampleMapOps.Transfer[
dest: sm,
destStart: loc,
source: [sampleMap: brush],
function: maskFcns[op]
];
};
PaintPixel: PUBLIC PROC [sm: SampleMap, loc: CVEC, op: Operation] ~ {
SampleMapOps.PutSample[sampleMap: sm, index: loc, value: 1, function: maskFcns[op]];
};
FillRectangle: PUBLIC PROC [sm: SampleMap, rect: Rectangle, op: Operation] ~ {
SampleMapOps.Fill[dest: [sm, [rect.sMin, rect.fMin], [rect.sMax-rect.sMin, rect.fMax-rect.fMin]], value: 1, function: maskFcns[op]];
};
TestDifferences: PROC [a, b: Rectangle] RETURNS [list: LIST OF Rectangle ← NIL] ~ {
Action: PROC [r: Rectangle] ~ {
list ← CONS[r, list];
};
Differences[a, b, Action];
};
Intersect: PUBLIC PROC [a, b: Rectangle] RETURNS [Rectangle] ~ {
RETURN [[sMin: MAX[a.sMin, b.sMin], fMin: MAX[a.fMin, b.fMin], sMax: MIN[a.sMax, b.sMax], fMax: MIN[a.fMax, b.fMax]]];
};
Differences: PUBLIC PROC [a, b: Rectangle, action: PROC [r: Rectangle]] ~ {
i: Rectangle ~ Intersect[a, b];
DoRectangle: PROC [q: Rectangle] ~ {
Quarantee that i is contained by q.
IF q.fMax>q.fMin THEN {
IF q.sMin<i.sMin THEN
 action[[sMin: q.sMin, sMax: i.sMin, fMin: q.fMin, fMax: q.fMax]];
IF q.sMax>i.sMax THEN
 action[[sMin: i.sMax, sMax: q.sMax, fMin: q.fMin, fMax: q.fMax]];
};
IF i.sMax>i.sMin THEN {
IF q.fMin<i.fMin THEN
 action[[sMin: i.sMin, sMax: i.sMax, fMin: q.fMin, fMax: i.fMin]];
IF q.fMax>i.fMax THEN
 action[[sMin: i.sMin, sMax: i.sMax, fMin: i.fMax, fMax: q.fMax]];
};
};
IF i.sMax<i.sMin OR i.fMax<i.fMin THEN {
IF a.sMax>a.sMin AND a.fMax>a.fMin THEN action[a];
IF b.sMax>b.sMin AND b.fMax>b.fMin THEN action[b];
}
ELSE {
DoRectangle[a];
DoRectangle[b];
};
IF a.sMin>b.sMax OR b.sMin>a.sMax OR a.fMin>b.fMax OR b.fMin>a.fMax THEN RETURN;
};
Apply a TRC
trcSize: CARDINAL ~ 256;
ApplyTRC: PUBLIC PROC [sm: SampleMap, sa: SampleArray, trcs: TRCSequence, bounds: Rectangle ← nullRectangle] ~ TRUSTED {
UnsafeLookup: UNSAFE PROC [tbl, src, dst: LONG POINTER TO Basics.RawWords, count: NAT] ~ UNCHECKED {
THROUGH [0..count/8) DO
dst[0] ← tbl[src[0]];
dst[1] ← tbl[src[1]];
dst[2] ← tbl[src[2]];
dst[3] ← tbl[src[3]];
dst[4] ← tbl[src[4]];
dst[5] ← tbl[src[5]];
dst[6] ← tbl[src[6]];
dst[7] ← tbl[src[7]];
src ← src+8;
dst ← dst+8;
ENDLOOP;
FOR k: NAT IN[0..count MOD 8) DO dst[k] ← tbl[src[k]] ENDLOOP;
};
fSize: CARDINAL;
scratchBuffer, bitBuffer: Buffer;
scratchPtr: LONG POINTER TO Basics.RawWords;
scratchMapByteKludge: SampleMap;
table: Buffer;
tablePtr: LONG POINTER TO Basics.RawWords;
IF sa.n#trcs.n THEN ERROR;
bounds ← Intersect[bounds, [sMin: 0, fMin: 0, sMax: sa.sSize-1, fMax: sa.fSize-1]];
fSize ← bounds.fMax+1-bounds.fMin;
bitBuffer ← SampleMapOps.ObtainBuffer[length: fSize];
scratchBuffer ← SampleMapOps.ObtainBuffer[length: fSize];
scratchPtr ← SampleMapOps.GetPointer[buffer: scratchBuffer, start: 0, count: fSize];
scratchMapByteKludge ← SampleMapOps.UnsafeCreate[sSize: 1, fSize: fSize*2, bitsPerSample: 8, bitsPerLine: fSize*16, base: [word: scratchPtr, bit: 0], nWords: fSize, ref: NIL];
table ← SampleMapOps.ObtainBuffer[length: trcSize*2];
tablePtr ← SampleMapOps.GetPointer[buffer: table, start: 0, count: trcSize*2];
FOR sample: NAT IN [0..trcSize) DO
table[sample] ← sample;
ENDLOOP;
FOR i: NAT IN [0..sa.n) DO
sep: SampleMap ~ sa.layer[i].sm;
LOOPHOLE[tablePtr+trcSize, LONG POINTER TO TRC]^ ← trcs[i]^;
FOR scan: CARDINAL IN [bounds.sMin .. bounds.sMax] DO
SampleMapOps.Get[buffer: bitBuffer, count: fSize, sampleMap: sm, s: scan, f: bounds.fMin];
SampleMapOps.Get[buffer: scratchBuffer, count: fSize, sampleMap: sep, s: scan, f: bounds.fMin];
SampleMapOps.Put[buffer: bitBuffer, count: fSize, sampleMap: scratchMapByteKludge, df: 2];
UnsafeLookup[tbl: tablePtr, src: scratchPtr, dst: scratchPtr, count: fSize];
SampleMapOps.Put[buffer: scratchBuffer, count: fSize, sampleMap: sep, s: scan, f: bounds.fMin];
ENDLOOP;
ENDLOOP;
};
The Fill and MatchColor procedures
OutOfRectangle: PUBLIC ERROR ~ CODE;
Direction: TYPE ~  { r, ur, u, ul, l, dl, d, dr};
next: ARRAY Direction OF Direction ~ [ur, u, ul, l, dl, d, dr, r];
prv: ARRAY Direction OF Direction ~ [dr, r, ur, u, ul, l, dl, d];
start: ARRAY Direction OF Direction ~ [dr, dr, r, r, ul, ul, dl, dl];
When one has just come from direction x, one needs to check next in direction start[x]. The spots for u and ul (both containing r) are anomolous: there is no possibility of finding a pixel there, but this way those directions will record themselves as left edges.
ds: ARRAY Direction OF INTEGER ~ [ 0, -1, -1, -1, 0, 1, 1, 1];
df: ARRAY Direction OF INTEGER ~ [ 1, 1, 0, -1, -1, -1, 0, 1];
Fill: PUBLIC PROC [sm: SampleMap, loc: CVEC, bounds: Rectangle ← nullRectangle] RETURNS [extent: Rectangle] ~ TRUSTED {
Basic Approach (taken from M. Bird and other Lispers):
1. Find a left edge.
2. For each edge not previously found:
a. Circumnavigate the edge, marking and remembering it, and stashing location of new left edges
b. Start moving right from the edge until you come to another edge.
sMin, sMax, fMin, fMax: CARDINAL;
filledMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1];
edgesMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1];
filled: ArrayHack ~ SampleMapToArrayHack[filledMap];
edges: ArrayHack ~ SampleMapToArrayHack[edgesMap];
mask: ArrayHack ~ SampleMapToArrayHack[sm];
leftEdges: LIST OF CVEC ← NIL;
IfRectangle: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE {
RETURN [~(v.s IN (sMin .. sMax) AND v.f IN (fMin .. fMax))]
};
IfEdge: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE {
RETURN [(mask[v.s][v.f] AND ~filled[v.s][v.f]) OR IfRectangle[v]]
};
RememberLeftEdgePos: PROC [p: CVEC] ~ TRUSTED INLINE {
leftEdges ← CONS[p, leftEdges];
extent.sMin ← MIN[extent.sMin, p.s];
extent.sMax ← MAX[extent.sMax, p.s];
extent.fMin ← MIN[extent.fMin, p.f];
extent.fMax ← MAX[extent.fMax, p.f]; --Max is check elsewhere
};
GetNextLeftEdgePos: PROC RETURNS [p: CVEC] ~ TRUSTED INLINE {
p ← leftEdges.first;
leftEdges ← leftEdges.rest;
};
Navigate: PROC [initial: CVEC] ~ TRUSTED {
IsSingleton: PROC RETURNS [BOOLEAN] ~ TRUSTED INLINE {
count: CARDINAL ← 0;
IF IfRectangle[initial] THEN RETURN [FALSE]; --We know it's not a singleton on the bounds edge, and it may be dangerous to check as below...
FOR f: INTEGER IN [-1..1] DO
FOR s: INTEGER IN [-1..1] DO
IF IfEdge[[s: initial.s+s, f: initial.f+f]] THEN count ← count+1;
ENDLOOP;
ENDLOOP;
RETURN [count <= 1]
};
Assumes you got here from the left.
direction, lastDirection: Direction ← dr; --(Choosing dr rather than r, so that the algorithm checks dl and d).
pos: CVEC ← initial;
IF edges[initial.s][initial.f] THEN RETURN; --We've already seen this edge
IF IsSingleton[] THEN {
RememberLeftEdgePos[initial];
RETURN;
};
Find the last direction to turn that places us on the initial square, so that main loop can test for it
lastDirection ← r;   --(Actually, know can't get this...)
UNTIL IfEdge[[s: pos.s-ds[lastDirection], f: pos.f-df[lastDirection]]] DO--Reversed sign is because we're moving into rather than out of pos.
lastDirection ← prv[lastDirection];
ENDLOOP;
DO
edges[pos.s][pos.f] ← TRUE;
direction ← start[direction];
DO
IF IfEdge[[s: pos.s+ds[direction], f: pos.f+df[direction]]] THEN EXIT
ELSE IF direction=r THEN RememberLeftEdgePos[pos]; --I.e., if we see a blank spot to our right, then this is a left edge
direction ← next[direction];
ENDLOOP;
Here, direction contain the next direction to move. Make the move.
pos ← [s: pos.s+ds[direction], f: pos.f+df[direction]];
See if this is the end of the line...
IF pos=initial AND direction=lastDirection THEN EXIT;
ENDLOOP;
IF lastDirection IN [ur..ul] THEN RememberLeftEdgePos[initial];
};
Main section.
SampleMapOps.Clear[filledMap];
SampleMapOps.Clear[edgesMap];
bounds ← Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]];
sMin ← bounds.sMin;
sMax ← bounds.sMax;
fMin ← bounds.fMin;
fMax ← bounds.fMax;
IF ~loc.s IN (sMin .. sMax) OR ~loc.f IN (fMin .. fMax) THEN ERROR OutOfRectangle[];
IF mask[loc.s][loc.f] THEN RETURN; --If it's already on, there's nothing we can do...
extent ← [sMin: loc.s, sMax: loc.s, fMin: loc.f, fMax: loc.f];
UNTIL mask[loc.s][loc.f] DO--Find a right edge
loc.f ← loc.f + 1;
ENDLOOP;
Navigate[loc];  --Navigate the first edge found
UNTIL leftEdges=NIL DO  --While left edge positions remain...
f, s: CARDINAL;
vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN;
[[s: s, f: f]] ← GetNextLeftEdgePos[];
vector ← mask[s];
f ← f+1;    --Start on blank square
UNTIL vector[f] OR f=fMax DO
vector[f] ← filled[s][f] ← TRUE;
f ← f+1;
ENDLOOP;
Here, [x, y] is on a right edge
extent.fMax ← MAX[extent.fMax, f];
Navigate[[s: s, f: f]];
ENDLOOP;
};
defaultColorDistance: PUBLIC CARDINAL ← 10;
MatchColorFill: PUBLIC PROC [sm: SampleMap, sa: SampleArray, loc: CVEC, bounds: Rectangle ← nullRectangle, colorDistance: CARDINAL ← defaultColorDistance] RETURNS [extent: Rectangle] ~ TRUSTED {
Basic Approach (taken from M. Bird and other Lispers):
1. Find a left edge.
2. For each edge not previously found:
a. Circumnavigate the edge, marking and remembering it, and stashing location of new left edges
b. Start moving right from the edge until you come to another edge.
sMin, sMax, fMin, fMax: CARDINAL;
filledMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1];
edgesMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1];
filled: ArrayHack ~ SampleMapToArrayHack[filledMap];
edges: ArrayHack ~ SampleMapToArrayHack[edgesMap];
mask: ArrayHack ~ SampleMapToArrayHack[sm];
redHack, greenHack, blueHack: SampleArrayHack;
leftEdges: LIST OF CVECNIL;
red, green, blue, g2r, g2b: INTEGER;
ColorDistance: PROC [r, g, b: CARDINAL] RETURNS [d: CARDINAL] ~ TRUSTED INLINE {
RETURN [ABS[INTEGER[g]-INTEGER[r] - g2r] + ABS[INTEGER[g]-INTEGER[b] - g2b]];
};
FetchRed: PROC [v: CVEC] RETURNS [value: CARDINAL] ~ TRUSTED INLINE {
RETURN [redHack[v.s][v.f]];
};
FetchGreen: PROC [v: CVEC] RETURNS [value: CARDINAL] ~ TRUSTED INLINE {
RETURN [greenHack[v.s][v.f]];
};
FetchBlue: PROC [v: CVEC] RETURNS [value: CARDINAL] ~ TRUSTED INLINE {
RETURN [blueHack[v.s][v.f]];
};
IfBounds: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE {
RETURN [~(v.s IN (sMin .. sMax) AND v.f IN (fMin .. fMax))]
};
IfEdge: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE {
RETURN [IfBounds[v] OR (ColorDistance[r: FetchRed[v], g: FetchGreen[v], b: FetchBlue[v]] > colorDistance)];
};
RememberLeftEdgePos: PROC [p: CVEC] ~ TRUSTED INLINE {
leftEdges ← CONS[p, leftEdges];
extent.sMin ← MIN[extent.sMin, p.s];
extent.sMax ← MAX[extent.sMax, p.s];
extent.fMin ← MIN[extent.fMin, p.f];
extent.fMax ← MAX[extent.fMax, p.f]; --Max is check elsewhere
};
GetNextLeftEdgePos: PROC RETURNS [p: CVEC] ~ TRUSTED INLINE {
p ← leftEdges.first;
leftEdges ← leftEdges.rest;
};
Navigate: PROC [initial: CVEC] ~ TRUSTED {
IsSingleton: PROC RETURNS [BOOLEAN] ~ TRUSTED INLINE {
count: CARDINAL ← 0;
IF IfBounds[initial] THEN RETURN [FALSE]; --We know it's not a singleton on the bounds edge, and it may be dangerous to check as below...
FOR f: INTEGER IN [-1..1] DO
FOR s: INTEGER IN [-1..1] DO
IF IfEdge[[s: initial.s+s, f: initial.f+f]] THEN count ← count+1;
ENDLOOP;
ENDLOOP;
RETURN [count <= 1]
};
Assumes you got here from the left.
direction, lastDirection: Direction ← dr; --(Choosing dr rather than r, so that the algorithm checks dl and d).
pos: CVEC ← initial;
IF edges[initial.s][initial.f] THEN RETURN; --We've already seen this edge
IF IsSingleton[] THEN {
RememberLeftEdgePos[initial];
RETURN;
};
Find the last direction to turn that places us on the initial square, so that main loop can test for it
lastDirection ← r;   --(Actually, know can't get this...)
UNTIL IfEdge[[s: pos.s-ds[lastDirection], f: pos.f-df[lastDirection]]] DO--Reversed sign is because we're moving into rather than out of pos.
lastDirection ← prv[lastDirection];
ENDLOOP;
DO
edges[pos.s][pos.f] ← TRUE;
direction ← start[direction];
DO
IF IfEdge[[s: pos.s+ds[direction], f: pos.f+df[direction]]] THEN EXIT
ELSE IF direction=r THEN RememberLeftEdgePos[pos]; --I.e., if we see a blank spot to our right, then this is a left edge
direction ← next[direction];
ENDLOOP;
Here, direction contain the next direction to move. Make the move.
pos ← [s: pos.s+ds[direction], f: pos.f+df[direction]];
See if this is the end of the line...
IF pos=initial AND direction=lastDirection THEN EXIT;
ENDLOOP;
IF lastDirection IN [ur..ul] THEN RememberLeftEdgePos[initial];
};
Main section.
IF sa.n#3 THEN ERROR;
redHack ← SampleMapToSampleArrayHack[sa[0].sm];
greenHack ← SampleMapToSampleArrayHack[sa[1].sm];
blueHack ← SampleMapToSampleArrayHack[sa[2].sm];
SampleMapOps.Clear[filledMap];
SampleMapOps.Clear[edgesMap];
bounds ← Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]];
sMin ← bounds.sMin;
sMax ← bounds.sMax;
fMin ← bounds.fMin;
fMax ← bounds.fMax;
IF ~loc.s IN (sMin .. sMax) OR ~loc.f IN (fMin .. fMax) THEN ERROR OutOfRectangle[];
extent ← [sMin: loc.s, sMax: loc.s, fMin: loc.f, fMax: loc.f];
Initial values for colors
red ← FetchRed[loc];
green ← FetchGreen[loc];
blue ← FetchBlue[loc];
g2r ← green - red;
g2b ← green - blue;
UNTIL IfEdge[loc] DO--Find a right edge
loc.f ← loc.f + 1;
ENDLOOP;
Navigate[loc];  --Navigate the first edge found
UNTIL leftEdges=NIL DO  --While left edge positions remain...
f, s: CARDINAL;
vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN;
[[s: s, f: f]] ← GetNextLeftEdgePos[];
vector ← mask[s];
f ← f+1;    --Start on blank square
UNTIL IfEdge[[s: s, f: f]] OR f=fMax DO
vector[f] ← filled[s][f] ← TRUE;
f ← f+1;
ENDLOOP;
Here, [x, y] is on a right edge
extent.fMax ← MAX[extent.fMax, f];
Navigate[[s: s, f: f]];
ENDLOOP;
};
MatchColor: PUBLIC PROC [sm: SampleMap, sa: SampleArray, rgb: RGB, bounds: Rectangle ← nullRectangle, colorDistance: CARDINAL ← defaultColorDistance] ~ TRUSTED {
mask: ArrayHack ~ SampleMapToArrayHack[sm];
redHack, greenHack, blueHack: SampleArrayHack;
red, green, blue: INTEGER;
ColorDistance: PROC [r, g, b: CARDINAL] RETURNS [d: CARDINAL] ~ TRUSTED INLINE {
RETURN [ABS[INTEGER[r] - red] + ABS[INTEGER[g]-green] + ABS[INTEGER[b] - blue]];
};
FetchRed: PROC [s, f: CARDINAL] RETURNS [value: CARDINAL] ~ TRUSTED INLINE {
RETURN [redHack[s][f]];
};
FetchGreen: PROC [s, f: CARDINAL] RETURNS [value: CARDINAL] ~ TRUSTED INLINE {
RETURN [greenHack[s][f]];
};
FetchBlue: PROC [s, f: CARDINAL] RETURNS [value: CARDINAL] ~ TRUSTED INLINE {
RETURN [blueHack[s][f]];
};
IF sa.n#3 THEN ERROR;
redHack ← SampleMapToSampleArrayHack[sa[0].sm];
greenHack ← SampleMapToSampleArrayHack[sa[1].sm];
blueHack ← SampleMapToSampleArrayHack[sa[2].sm];
Initial values for colors
red ← rgb.r;
green ← rgb.g;
blue ← rgb.b;
bounds ← Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]];
FOR s: CARDINAL IN [bounds.sMin .. bounds.sMax] DO
vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN ~ mask[s];
FOR f: CARDINAL IN [bounds.fMin .. bounds.fMax] DO
IF ColorDistance[r: FetchRed[s: s, f: f], g: FetchGreen[s: s, f: f], b: FetchBlue[s: s, f: f]] < colorDistance THEN vector[f] ← TRUE;
ENDLOOP;
ENDLOOP;
};
DiscriminateColor: PUBLIC PROC [sm: SampleMap, sa: SampleArray, near, far: RGB, bounds: Rectangle ← nullRectangle] ~ TRUSTED {
nearR: INTEGER ~ near.r;
nearG: INTEGER ~ near.g;
nearB: INTEGER ~ near.b;
farR: INTEGER ~ far.r;
farG: INTEGER ~ far.g;
farB: INTEGER ~ far.b;
mask: ArrayHack ~ SampleMapToArrayHack[sm];
redHack, greenHack, blueHack: SampleArrayHack;
Near: PROC [r, g, b: CARDINAL] RETURNS [BOOLEAN] ~ TRUSTED INLINE {
RETURN [
ABS[INTEGER[r] - nearR] + ABS[INTEGER[g]-nearG] + ABS[INTEGER[b] - nearB]
< ABS[INTEGER[r] - farR] + ABS[INTEGER[g]-farG] + ABS[INTEGER[b] - farB]
];
};
IF sa.n#3 THEN ERROR;
redHack ← SampleMapToSampleArrayHack[sa[0].sm];
greenHack ← SampleMapToSampleArrayHack[sa[1].sm];
blueHack ← SampleMapToSampleArrayHack[sa[2].sm];
bounds ← Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]];
FOR s: CARDINAL IN [bounds.sMin .. bounds.sMax] DO
vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN ~ mask[s];
FOR f: CARDINAL IN [bounds.fMin .. bounds.fMax] DO
IF Near[r: redHack[s][f], g: greenHack[s][f], b: blueHack[s][f]] THEN vector[f] ← TRUE;
ENDLOOP;
ENDLOOP;
};
SampleMapToSampleArrayHack: PROC [sm: SampleMap] RETURNS [sah: SampleArrayHack] ~ TRUSTED {
ref: REF ~ RefTab.Fetch[x: sampleMapToSampleArrayHack, key: sm].val;
IF ref=NIL THEN {
ptr: LONG POINTER ← sm.base.word;
offset: NAT ← sm.bitsPerLine/16;
IF sm.base.bit#0 OR sm.bitsPerSample#8 THEN ERROR;
sah ← NEW[SampleArrayHackRep[sm.sSize]];
FOR i: NAT IN [0..sah.n) DO
sah[i] ← ptr;
ptr ← ptr+offset;
ENDLOOP;
[] ← RefTab.Store[x: sampleMapToSampleArrayHack, key: sm, val: sah];
}
ELSE sah ← NARROW[ref];
};
SampleMapToArrayHack: PROC [sm: SampleMap] RETURNS [array: ArrayHack] ~ TRUSTED {
ptr: LONG POINTER ← sm.base.word;
offset: NAT ← sm.bitsPerLine/16;
IF sm.base.bit#0 OR sm.bitsPerSample#1 THEN ERROR;
array ← NEW[ArrayHackRep[sm.sSize]];
FOR i: NAT IN [0..array.n) DO
array[i] ← ptr;
ptr ← ptr+offset;
ENDLOOP;
};
END.