ImagerSample.mesa
Copyright Ó 1985, 1986, 1987, 1988, 1989, 1991 by Xerox Corporation. All rights reserved.
Michael Plass, August 16, 1991 2:47 pm PDT
Doug Wyatt, March 7, 1986 2:41:21 pm PST
DIRECTORY
Basics USING [bitsPerWord, RawWords, UnsafeBlock],
RasterBasics USING [BitAddress, DstFunc, SrcFunc],
Scaled USING [Value],
SF USING [Box, BoxAction, BoxGenerator, maxVec, minVec, Vec, zeroVec];
ImagerSample: CEDAR DEFINITIONS
~ BEGIN
Vec: TYPE ~ SF.Vec;
minVec: Vec ~ SF.minVec;
zeroVec: Vec ~ SF.zeroVec;
maxVec: Vec ~ SF.maxVec;
Box: TYPE ~ SF.Box;
maxBox: Box ~ [min: minVec, max: maxVec];
BoxAction: TYPE ~ SF.BoxAction;
BoxGenerator: TYPE ~ SF.BoxGenerator;
maxCount: NAT ~ NAT.LAST;
MultipleReleaseOfScratch: ERROR;
Samples and SampleBuffers
Sample: TYPE ~ WORD;
maxSample: Sample ~ Sample.LAST;
RawSamples: TYPE ~ Basics.RawWords;
maxBitsPerSample: NAT ~ Basics.bitsPerWord;
BitsPerSample: TYPE ~ NAT[0..maxBitsPerSample];
SampleBuffer: TYPE ~ REF SampleBufferRep;
SampleBufferRep: TYPE ~ RECORD [
length: NAT,
samples: SEQUENCE maxLength: NAT OF Sample
];
BitAddress: TYPE ~ RasterBasics.BitAddress;
NewSamples: PROC [length: NAT, scratch: SampleBuffer ¬ NIL] RETURNS [SampleBuffer];
ObtainScratchSamples: PROC [length: NAT] RETURNS [SampleBuffer];
ReleaseScratchSamples: PROC [buffer: SampleBuffer];
DoWithScratchSamples: PROC [length: NAT, action: PROC [SampleBuffer]];
PointerToSamples: PROC [buffer: SampleBuffer, start, count: NAT] RETURNS [LONG POINTER TO RawSamples];
FlipSamples: PROC [buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount];
Reverses the order of the samples in the buffer.
FillSamples: PROC [buffer: SampleBuffer, value: Sample, start: NAT ¬ 0, count: NAT ¬ maxCount];
CopySamples: PROC [dst, src: SampleBuffer, dstStart, srcStart: NAT ¬ 0, count: NAT ¬ maxCount];
ClearSamples: PROC [buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount];
SampleMap creation
SampleMap: TYPE ~ REF SampleMapRep;
SampleMapRep: TYPE ~ PRIVATE RECORD [
-- It is UNSAFE to alter these fields. --
box: Box, -- lower and upper bounds in each direction
immutable: BOOL,
bitsPerSample: [0..BITS[Sample]], -- number of bits per sample
v: SELECT tag: * FROM
raster => [
bitsPerLine: CARD, -- bits per scan line
base: BitAddress, -- starting bit address
ref: REF -- for retaining the underlying storage
],
object => [class: SampleMapClass, data: REF],
ENDCASE
];
This type defines a two-dimensional array of samples. To avoid confusion no matter how the represented image is oriented, terms like height and width are avoided, and the coordinates are expressed in terms of s and f, for slow and fast. On a display, the s axis is normally vertical, and points down, but on a printer that scans in the long direction, the f axis will point up. The samples are indexed by s in the range [box.min.s..box.max.s) and f in the range [box.min.f..box.max.f).
SampleMapClass: TYPE ~ REF SampleMapClassRep;
SampleMapClassRep: TYPE; -- Opaque to allow more flexibility
ObjectGetProc: TYPE ~ PROC [self: ObjectSampleMap, initIndex: SF.Vec, buffer: SampleBuffer, start, count: NAT];
ObjectPutProc: TYPE ~ PROC [self: ObjectSampleMap, initIndex: SF.Vec, buffer: SampleBuffer, start, count: NAT, function: Function];
RasterSampleMap: TYPE ~ REF SampleMapRep.raster;
ObjectSampleMap: TYPE ~ REF SampleMapRep.object;
UnsafeNewSampleMap: UNSAFE PROC [box: Box, bitsPerSample: BitsPerSample,
bitsPerLine: INT, base: BitAddress, ref: REF, words: INT,
scratchDescriptor: SampleMap ¬ NIL] RETURNS [RasterSampleMap];
It is the client's responsibility to ensure that an appropriate block of storage is provided; the parameters will be checked for internal consistency; words declares the number of words of storage supplied, starting at base.word; scratchDescriptor may be provided to avoid the need to allocate a new descriptor.
BitsForSamples: PROC [fSize: NAT, bitsPerSample: NAT] RETURNS [INT];
Returns number of bits for fSize samples, rounded up to a whole number of words.
WordsForLines: PROC [sSize: NAT, bitsPerLine: INT] RETURNS [INT];
Returns number of words for sSize scan lines, starting at a word boundary.
WordsForMap: PROC [size: Vec, bitsPerSample: BitsPerSample ¬ 1,
bitsPerLine: INT ¬ 0] RETURNS [INT];
Returns number of words required for a new sample map of given size.
If bitsPerLine=0, bitsPerLine ← BitsForSamples[size.f, bitsPerSample].
NewSampleMap: PROC [box: Box, bitsPerSample: BitsPerSample ¬ 1, bitsPerLine: INT ¬ 0] RETURNS [RasterSampleMap];
Allocates counted storage or counted VM, as needed.
If bitsPerLine=0, bitsPerLine ← BitsForSamples[size.f, bitsPerSample].
NewObjectSampleMap: PROC [box: Box, bitsPerSample: BitsPerSample, getSamples: ObjectGetProc, putSamples: ObjectPutProc, data: REF] RETURNS [ObjectSampleMap];
Allocates a object-oriented sampleMap.
Copy: PROC [map: SampleMap, delta: Vec ¬ zeroVec, box: Box ¬ maxBox] RETURNS [RasterSampleMap];
Allocates a new map and copies the samples.
Get[map, p] = Get[result, SF.Add[p, delta]]
result.box = SF.Intersect[box, SF.Displace[map.box, delta]]
ObtainScratchMap: PROC [box: Box, bitsPerSample: BitsPerSample ¬ 1, bitsPerLine: INT ¬ 0]
RETURNS [RasterSampleMap];
ReleaseScratchMap: PROC [map: SampleMap];
DoWithScratchMap: PROC [box: Box, bitsPerSample: BitsPerSample ¬ 1, action: PROC [RasterSampleMap]];
SampleMap descriptor operations
ObtainUnsafeDescriptor: UNSAFE PROC [size: Vec, bitsPerSample: BitsPerSample,
bitsPerLine: INT, base: BitAddress, ref: REF, words: INT, rawMin, delta: Vec ¬ zeroVec] RETURNS [RasterSampleMap];
The same caveats apply as for UnsafeNewSampleMap
the descriptor is allocated from the scratch pool
words declares the number of words of storage supplied, starting at base.word
rawMin tells which sample in the raw storage is to be the first actual sample
delta will become result.box.min
ReIndex: PROC [map: SampleMap, delta: Vec ¬ zeroVec, box: Box ¬ maxBox] RETURNS [SampleMap];
Creates a new descriptor on all or part of the same sample storage
the descriptor is allocated from the scratch pool
Get[map, p] = Get[result, SF.Add[p, delta]]
result.box = SF.Intersect[box, SF.Displace[map.box, delta]]
Clip: PROC [map: SampleMap, box: Box ¬ maxBox] RETURNS [SampleMap];
ReIndex[map: map, box: box]
Shift: PROC [map: SampleMap, delta: Vec ¬ zeroVec] RETURNS [SampleMap];
ReIndex[map: map, delta: delta]
ZeroOrigin: PROC [map: SampleMap] RETURNS [SampleMap];
ReIndex[map: temp, delta: SF.Neg[map.box.min]]
ReleaseDescriptor: UNSAFE PROC [map: SampleMap];
Releases descriptor to a small pool.
Unsafe to use the SampleMap any more after this is called.
SampleMap selectors
GetBox: PROC [map: SampleMap] RETURNS [Box]
~ INLINE {RETURN [map.box]};
GetSize: PROC [map: SampleMap] RETURNS [Vec]
~ INLINE {b: Box ~ map.box; RETURN [[s: b.max.s-b.min.s, f: b.max.f-b.min.f]]};
GetImmutable: PROC [map: SampleMap] RETURNS [BOOL]
~ INLINE {RETURN [map.immutable]};
MakeImmutable: PROC [map: SampleMap] ~ INLINE {map.immutable ¬ TRUE};
GetBitsPerSample: PROC [map: SampleMap] RETURNS [BitsPerSample]
~ INLINE {RETURN [map.bitsPerSample]};
GetBitsPerLine: PROC [map: RasterSampleMap] RETURNS [INT]
~ INLINE {RETURN [map.bitsPerLine]};
GetBase: PROC [map: RasterSampleMap] RETURNS [BitAddress]
~ INLINE {RETURN [map.base]};
GetUnsafeBlock: PROC [map: RasterSampleMap] RETURNS [Basics.UnsafeBlock];
For easier I/O of samplemaps.
Error if initial sample is not byte-aligned or if the scanlines are not contiguous. May include up to 7 bits of padding at the end.
GetRef: PROC [map: RasterSampleMap] RETURNS [REF]
~ INLINE {RETURN [map.ref]};
GetData: PROC [map: ObjectSampleMap] RETURNS [REF]
~ INLINE {RETURN [map.data]};
SampleMap sample access
SrcFunc: TYPE ~ RasterBasics.SrcFunc;
DstFunc: TYPE ~ RasterBasics.DstFunc;
Function: TYPE ~ MACHINE DEPENDENT RECORD [dstFunc: DstFunc, srcFunc: SrcFunc];
nullFunction: Function ~ [dstFunc: null, srcFunc: null];
[null, null] dst ← src
[or, null] dst ← BITOR[dst, src]
[and, null] dst ← BITAND[dst, src]
[and, complement] dst ← BITAND[dst, BITNOT[src]]
[xor, null] dst ← BITXOR[dst, src]
et cetera.
ApplyFunction: PROC [function: Function, dstVal, srcVal: Sample] RETURNS [Sample];
Applies a Function to a pair of samples.
Get: PROC [map: SampleMap, index: Vec] RETURNS [Sample];
Gets the indexed sample from the map.
BoundsFault if NOT SF.In[index, map.box]
Put: PROC [map: SampleMap, index: Vec, value: Sample, function: Function ¬ nullFunction];
Puts modified value into the map at indexed location, dropping extra high-order bits.
BoundsFault if NOT SF.In[index, map.box]
GetSamples: PROC [map: SampleMap, initIndex: Vec ¬ zeroVec, delta: Vec ¬ [s: 0, f: 1],
buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount];
Gets a run of samples from the map into a buffer. The effect is:
FOR j: NAT IN [0..MIN[buffer.length-start, count]) DO
p: Vec ~ [s: j*delta.s, f: j*delta.f];
buffer[start+j] ← map.Get[initIndex.Add[p]];
ENDLOOP;
except that any BoundsFault may be raised before any samples are moved.
GetTileSamples: PROC [tile: SampleMap, phase: NAT ¬ 0, initIndex: SF.Vec ¬ zeroVec, buffer: SampleBuffer];
tempBox: Box ~ [min: initIndex, max: initIndex.Add[[1, buffer.length]]];
temp: SampleMap ~ NewSampleMap[box: tempBox, bitsPerSample: tile.bitsPerSample];
Tile[map: temp, tile: tile, phase: phase];
GetSamples[map: temp, initIndex: initIndex, buffer: buffer]
Halftone: PROC [map: SampleMap, min: SF.Vec, sampleBuffer, thresholdBuffer: SampleBuffer, function: Function];
FOR j: NAT IN [0..MIN[samples.length, thresholds.length]) DO
bit: [0..1] ~ IF sampleBuffer[j] > thresholdBuffer[j] THEN 0 ELSE 1;
Put[map, min.Add[[0, j]], bit, function];
ENDLOOP;
PutSamples: PROC [map: SampleMap, initIndex: Vec ¬ zeroVec, delta: Vec ¬ [s: 0, f: 1],
buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount,
function: Function ¬ nullFunction];
Stores a run of samples from a buffer into the map. The effect is:
FOR j: NAT IN [0..MIN[buffer.length-start, count]) DO
p: Vec ~ [s: j*delta.s, f: j*delta.f];
map.Put[initIndex.Add[p], buffer[start+j], function];
ENDLOOP;
except that any BoundsFault may be raised before any samples are moved.
SampleMap block operations
Clear: PROC [map: SampleMap];
A fast way to clear a map to zero.
Clears the padding bits on both ends of the scan lines as well as the actual samples.
Does not clear words that are entirely unused.
BasicTransfer: PROC [dst: SampleMap, src: SampleMap, dstMin: Vec ¬ zeroVec, srcMin: Vec ¬ zeroVec,
size: Vec, function: Function ¬ nullFunction];
Transfers samples to dst from src. The effect is:
FOR s: INTEGER IN [0..size.s) DO
FOR f: INTEGER IN [0..size.f) DO
p: Vec ~ [s: s, f: f];
value: Sample ~ Get[src, p.Add[srcMin]];
Put[dst, p.Add[dstMin], value, function];
ENDLOOP;
ENDLOOP;
except that if a bounds fault occurs, it occurs before any samples are moved.
Transfer: PROC [dst: SampleMap, src: SampleMap, delta: Vec ¬ zeroVec, function: Function ¬ nullFunction];
Transfers samples to dst from src. The effect is:
box: Box ~ SF.Intersect[dst.box, SF.Displace[src.box, delta]];
BasicTransfer[dst: dst, src: src, dstMin: box.min, srcMin: SF.Sub[box.min, delta], size: SF.Size[box], function: function];
Move: PROC [map: SampleMap,
dstMin: Vec ¬ zeroVec, srcMin: Vec ¬ zeroVec, size: Vec ¬ maxVec,
function: Function ¬ nullFunction];
The effect is:
BasicTransfer[dst: map, src: Copy[map], dstMin: dstMin, srcMin: srcMin,
size: size, function: function];
(avoids ripple if the source and destination overlap)
Fill: PROC [map: SampleMap, box: Box ¬ maxBox, value: Sample ¬ maxSample,
function: Function ¬ nullFunction];
Fills a box with a constant sample value. The effect is:
actualBox: Box ~ Intersect[map.box, box];
FOR s: INTEGER IN [actualBox.min.s..actualBox.max.s) DO
FOR f: INTEGER IN [actualBox.min.f..actualBox.max.f) DO
Put[map, [s, f], value, function];
ENDLOOP;
ENDLOOP;
SampleMap raw transfer
RawDescriptor: TYPE ~ RECORD [box: Box, bitsPerLine: NAT, basePointer: LONG POINTER, ref: REF];
The ref may be used to prevent premature GC of the memory that basePointer points into; RawListTransfer does not depend on it being provided.
RawListTransfer: PROC [dst: RasterSampleMap, src: LIST OF RawDescriptor, function: Function ¬ nullFunction];
Tile operations
TileFromStipple: PROC [stipple: WORD, bitsPerSample: BitsPerSample ¬ 1,
value0: Sample ¬ 0, value1: Sample ¬ maxSample,
scratch: SampleMap ¬ NIL] RETURNS [SampleMap];
Makes a tile from a 4-by-4 pattern.
Tile: PROC [map: SampleMap, box: Box ¬ maxBox,
tile: SampleMap, phase: NAT ¬ 0,
function: Function ¬ nullFunction];
Tiles the box with the pattern designated by (tile, phase). The effect is:
actualBox: Box ~ Intersect[map.box, box];
tileSize: Vec ~ Size[tile.box];
s0: INT ~ tile.box.min.s;
f0: INT ~ tile.box.min.f;
FOR s: INTEGER IN [actualBox.min.s..actualBox.max.s) DO
FOR f: INTEGER IN [actualBox.min.f..actualBox.max.f) DO
sTile: INT ~ s-s0;
fTile: INT ~ f-(f0+phase*(sTile div tileSize.s));
value: Sample ~ tile.Get[[s0 + sTile mod tileSize.s, f0 + fTile mod tileSize.f]];
map.Put[[s, f], value, function];
ENDLOOP;
ENDLOOP;
For div and mod, the sign of the remainder is the sign of the divisor, not the dividend.
TileBoxes: PROC [map: SampleMap, boxes: BoxGenerator,
tile: SampleMap, phase: NAT ¬ 0,
function: Function ¬ nullFunction];
Tiles each of the boxes. The effect is:
tileBox: PROC [box: Box] ~ {
true: [TRUE..TRUE] ~ SF.Inside[box, map.box]
Tile[map, box, tile, phase, function]
};
boxes[tileBox];
SampleMap area operations
TransferBoxes: PROC [dst: SampleMap, src: SampleMap, delta: Vec ¬ zeroVec,
boxes: BoxGenerator, function: Function ¬ nullFunction];
Transfers several boxes. The effect is:
transferBox: PROC [box: Box] ~ {
BasicTransfer[dst: dst, src: src, dstMin: box.min, srcMin: SF.Sub[box.min, delta], size: Size[box], function: function];
};
boxes[transferBox];
Note a bounds fault occurs if the BoxGenerator provides a box that is not inside the bounds of both dst and the displaced src, the assumption being that the client has already clipped the boxes.
FillBoxes: PROC [map: SampleMap, boxes: BoxGenerator, value: Sample ¬ maxSample,
function: Function ¬ nullFunction];
Fills several boxes. The effect is:
fillBox: PROC [box: Box] ~ {
true: [TRUE..TRUE] ~ SF.Inside[box, map.box];
Fill[map, box, value, function];
};
boxes[fillBox];
EdgeAction: TYPE ~ PROC [
which: [0..1], -- Which side this specifies
sMin: INTEGER, -- First scan line touched
sCount: NAT, -- Number of scan lines touched
f0: Scaled.Value, -- Initial value of f, offset by 1/2 pixel
df: Scaled.Value -- df/ds
];
RegionFill: PROC [dst: RasterSampleMap, edgeGenerator: PROC [EdgeAction], value: Sample ¬ maxSample, function: Function ¬ nullFunction];
Fills a monotone region designated by the edges. The edgeGenerator must specify the edges in nondecreasing sMin order. Clips to the destination bounds.
BoxesFromBitmap: PROC [map: SampleMap, boxAction: BoxAction];
Generates boxes that cover all of the non-zero samples in the SampleMap, and none of the zeros.
SampleMap scanning operations
Trim: PROC [map: SampleMap, box: Box ¬ maxBox, background: Sample ¬ 0] RETURNS [Box];
For removing a constant border.
All of the samples inside Intersect[map.box, box] and outside the resulting box will be equal to background.
IsAll: PROC [map: SampleMap, box: Box ¬ maxBox, value: Sample ¬ 0] RETURNS [BOOL];
Tests for a constant.
Equal: PROC [s1, s2: SampleMap] RETURNS [BOOL];
Tests for equality. Equal if s1.box = s2.box and Get[s1, p] = Get[s2, p] for all p in the common box. Note bitsPerSample need not match.
END.