DIRECTORY
Basics USING [bitsPerWord, RawWords],
CountedVM USING [Handle],
ImagerTransformation USING [Transformation],
PrincOps USING [BitAddress, SrcFunc, DstFunc],
SF USING [Box, BoxAction, BoxGenerator, maxVec, Vec, zeroVec],
Terminal USING [FrameBuffer];
~
BEGIN
Transformation: TYPE ~ ImagerTransformation.Transformation;
Vec: TYPE ~ SF.Vec;
zeroVec: Vec ~ SF.zeroVec;
maxVec: Vec ~ SF.maxVec;
Box: TYPE ~ SF.Box;
maxBox: Box ~ [min: zeroVec, 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
];
NewSamples:
PROC [length:
NAT,
scratch: SampleBuffer ←
NIL]
RETURNS [SampleBuffer];
ObtainScratchSamples:
PROC [length:
NAT]
RETURNS [SampleBuffer];
ReleaseScratchSamples:
PROC [samples: SampleBuffer];
DoWithScratchSamples:
PROC [length:
NAT, action:
PROC [SampleBuffer]];
PointerToSamples:
PROC [samples: SampleBuffer, start, count:
NAT]
RETURNS [
LONG
POINTER
TO RawSamples];
FlipSamples:
PROC [samples: SampleBuffer];
FillSamples:
PROC [samples: SampleBuffer, value: Sample,
start:
NAT ← 0, count:
NAT ← maxCount];
SampleMap creation
SampleMap: TYPE ~ REF SampleMapRep;
SampleMapRep:
TYPE ~
RECORD [
-- It is unsafe for clients to alter these fields. --
size: Vec, -- [s: <number of scan lines>, f: <samples per scan line>]
bitsPerSample: NAT, -- number of bits per sample
bitsPerLine: NAT, -- bits per scan line
base: PrincOps.BitAddress, -- starting bit address
ref: REF -- for retaining the underlying storage
];
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.
UnsafeNewSampleMap:
UNSAFE
PROC [size: Vec, bitsPerSample: BitsPerSample,
bitsPerLine:
NAT, base: PrincOps.BitAddress, ref:
REF, words:
INT,
scratchDescriptor: SampleMap ←
NIL]
RETURNS [SampleMap];
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 [
NAT];
Returns number of bits for fSize samples, rounded up to a whole number of words.
WordsForLines:
PROC [sSize:
NAT, bitsPerLine:
NAT]
RETURNS [
INT];
Returns number of words for sSize scan lines, starting at a word boundary.
WordsForMap:
PROC [size: Vec, bitsPerSample: BitsPerSample ← 1,
bitsPerLine:
NAT ← 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 [size: Vec, bitsPerSample: BitsPerSample ← 1, bitsPerLine:
NAT ← 0,
scratch: SampleMap ←
NIL]
RETURNS [SampleMap];
Allocates counted storage or counted VM, as needed.
If bitsPerLine=0, bitsPerLine ← BitsForSamples[size.f, bitsPerSample].
MapFromVM:
PROC [vm: CountedVM.Handle,
size: Vec, bitsPerSample: BitsPerSample ← 1, bitsPerLine:
NAT ← 0
]
RETURNS [SampleMap];
Like NewSampleMap, but uses client-supplied VM.
BoundsFault if insufficent storage is supplied.
MapFromFrameBuffer:
PROC [frameBuffer: Terminal.FrameBuffer]
RETURNS [SampleMap];
Creates a descriptor for the frame buffer.
SubMap:
PROC [self: SampleMap, box: Box ← maxBox]
RETURNS [SampleMap];
Creates a new descriptor on the same sample storage.
CopyMap:
PROC [self: SampleMap, box: Box ← maxBox]
RETURNS [SampleMap];
Allocates a new map and copies the samples.
ObtainScratchMap:
PROC [size: Vec, bitsPerSample: BitsPerSample ← 1]
RETURNS [SampleMap];
ReleaseScratchMap:
PROC [self: SampleMap];
DoWithScratchMap:
PROC [size: Vec, bitsPerSample: BitsPerSample ← 1,
action:
PROC [SampleMap]];
SampleMap operations
Function: TYPE ~ RECORD [dstFunc: PrincOps.DstFunc, srcFunc: PrincOps.SrcFunc];
nullFunction: Function ~ [null, 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]
etcetera.
Get:
PROC [self: SampleMap, index: Vec]
RETURNS [Sample];
Gets the indexed sample from the map.
BoundsFault if index.s NOT IN[0..self.size.s) OR index.f NOT IN[0..self.size.f)
Put:
PROC [self: SampleMap, index: Vec, value: Sample,
function: Function ← nullFunction];
Puts modified value into the map at indexed location, dropping extra high-order bits.
BoundsFault if index.s NOT IN[0..self.size.s) OR index.f NOT IN[0..self.size.f)
GetSamples:
PROC [self: SampleMap, min: Vec ← zeroVec, delta: Vec ← [s: 0, f: 1],
samples: 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[samples.length-start, count]) DO
offset: Vec ~ [s: j*delta.s, f: j*delta.f];
samples[start+j] ← self.Get[min.Add[offset]];
ENDLOOP;
PutSamples:
PROC [self: SampleMap, min: Vec ← zeroVec, delta: Vec ← [s: 0, f: 1],
samples: 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[samples.length-start, count]) DO
offset: Vec ~ [s: j*delta.s, f: j*delta.f];
self.Put[min.Add[offset], samples[start+j], function];
ENDLOOP;
Clear:
PROC [self: 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.
Transfer:
PROC [dst: SampleMap, src: SampleMap,
dstMin: Vec ← zeroVec, srcMin: Vec ← zeroVec, size: Vec ← maxVec,
function: Function ← nullFunction];
Transfers samples to dst from src. The effect is:
actualSize: Vec ~ NonNegative[Min3[size, dst.size.Sub[dstMin], src.size.Sub[srcMin]]]
FOR s: NAT IN [0..actualSize.s) DO
FOR f: NAT IN [0..actualSize.f) DO
offset: Vec ~ [s: s, f: f];
value: Sample ~ src.Get[srcMin.Add[offset]];
dst.Put[dstMin.Add[offset], value, function];
ENDLOOP;
ENDLOOP;
TransferBoxes:
PROC [dst: SampleMap, src: SampleMap,
boxes: BoxGenerator, dstOffset: Vec ← zeroVec, srcOffset: Vec ← zeroVec,
function: Function ← nullFunction];
Transfers several boxes. The effect is:
transferBox: PROC [box: Box] ~ {
dstMin: Vec ~ box.min.Add[dstOffset];
srcMin: Vec ~ box.min.Add[srcOffset];
Transfer[dst, src, dstMin, srcMin, box.Size, function];
};
boxes[transferBox];
Move:
PROC [self: SampleMap,
dstMin: Vec ← zeroVec, srcMin: Vec ← zeroVec, size: Vec ← maxVec,
function: Function ← nullFunction];
Like Transfer with dst=src=self, but avoids ripple if the source and destination overlap.
MoveBoxes:
PROC [self: SampleMap,
boxes: BoxGenerator, dstOffset: Vec ← zeroVec, srcOffset: Vec ← zeroVec,
function: Function ← nullFunction];
Moves several boxes; ds=(src.s-dst.s), df=(src.f-dst.f). The effect is:
moveBox: PROC [box: Box] ~ {
dstMin: Vec ~ box.min.Add[dstOffset];
srcMin: Vec ~ box.min.Add[srcOffset];
Move[self, dstMin, srcMin, box.Size, function];
};
boxes[moveBox];
Fill:
PROC [self: SampleMap, box: Box ← maxBox, value: Sample ← maxSample,
function: Function ← nullFunction];
Fills a box with a constant sample value. The effect is:
actualSize: Vec ~ NonNegative[self.size.Min[box.max].Sub[box.min]]
FOR s: NAT IN [0..actualSize.s) DO
FOR f: NAT IN [0..actualSize.f) DO
offset: Vec ~ [s: s, f: f];
self.Put[box.min.Add[offset], value, function];
ENDLOOP;
ENDLOOP;
FillBoxes:
PROC [self: SampleMap, boxes: BoxGenerator, value: Sample ← maxSample,
function: Function ← nullFunction];
Fills several boxes. The effect is:
fillBox: PROC [box: Box] ~ {
Fill[self, box, value, function];
};
boxes[fillBox];
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 [self: SampleMap, box: Box ← maxBox,
tile: SampleMap, s0, f0:
INT ← 0, phase:
NAT ← 0,
function: Function ← nullFunction];
Tiles the box with the pattern designated by (tile, s0, f0, phase). The effect is:
actualSize: Vec ~ NonNegative[self.size.Min[box.max].Sub[box.min]]
FOR s: NAT IN [box.min.s..box.min.s+actualSize.s) DO
FOR f: NAT IN [box.min.f..box.min.f+actualSize.f) DO
sTile: INT ~ s-s0;
fTile: INT ~ f-(f0+phase*(sTile div tile.size.s));
value: Sample ~ tile.Get[[sTile mod tile.size.s, fTile mod tile.size.f]];
self.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 [self: SampleMap, boxes: BoxGenerator,
tile: SampleMap, s0, f0:
INT ← 0, phase:
NAT ← 0,
function: Function ← nullFunction];
Tiles each of the boxes. The effect is:
tileBox: PROC [box: Box] ~ {
Tile[self, box, tile, s0, f0, phase, function]
};
boxes[tileBox];
Trim:
PROC [self: SampleMap, box: Box ← maxBox, background: Sample ← 0]
RETURNS [Box];
For removing a constant border.
All of the samples outside the resulting box will be equal to background.
IsAll:
PROC [self: SampleMap, box: Box ← maxBox, value: Sample ← 0]
RETURNS [
BOOL];
Tests for a constant.
Equal:
PROC [map1: SampleMap, box1: Box ← maxBox,
map2: SampleMap, box2: Box ← maxBox]
RETURNS [
BOOL];
Tests for equality.
Pixels, PixelBuffers and PixelMaps
PixelProc: TYPE ~ PROC [i: NAT] RETURNS [Sample];
PixelBuffer: TYPE ~ REF PixelBufferRep;
PixelBufferRep:
TYPE ~
RECORD [
length: NAT,
sampleBuffers: SEQUENCE samplesPerPixel: NAT OF SampleBuffer
];
NewPixels:
PROC [samplesPerPixel:
NAT, length:
NAT,
scratch: PixelBuffer ←
NIL]
RETURNS [PixelBuffer];
ObtainScratchPixels:
PROC [samplesPerPixel:
NAT, length:
NAT]
RETURNS [PixelBuffer];
ReleaseScratchPixels:
PROC [pixels: PixelBuffer];
DoWithScratchPixels:
PROC [samplesPerPixel:
NAT, length:
NAT, action:
PROC [PixelBuffer]];
PixelMap: TYPE ~ REF PixelMapRep;
PixelMapRep:
TYPE ~
RECORD [
size: Vec,
sampleMaps: SEQUENCE samplesPerPixel: NAT OF SampleMap
];
NewPixelMap:
PROC [samplesPerPixel:
NAT, size: Vec, maxSample: PixelProc,
scratch: PixelMap ←
NIL]
RETURNS [PixelMap];
GetPixels:
PROC [self: PixelMap, min: Vec ← zeroVec, delta: Vec ← [s: 0, f: 1],
pixels: PixelBuffer, start:
NAT ← 0, count:
NAT ← maxCount];
Gets a run of pixels from a pixel map into a pixel buffer. The effect is:
FOR i: NAT IN [0..self.samplesPerPixel) DO
self[i].GetSamples[min, delta, pixels[i], start, count];
ENDLOOP;
PutPixels:
PROC [self: PixelMap, min: Vec ← zeroVec, delta: Vec ← [s: 0, f: 1],
pixels: PixelBuffer, start:
NAT ← 0, count:
NAT ← maxCount,
function: Function ← nullFunction];
Stores a run of pixels into a pixel map from a pixel buffer. The effect is:
FOR i: NAT IN [0..self.samplesPerPixel) DO
self[i].PutSamples[min, delta, pixels[i], start, count, function];
ENDLOOP;
ResampleAction:
TYPE ~
PROC [pixels: PixelBuffer, min: Vec];
Resample: PROC [self: PixelMap, m: Transformation, interpolate: BOOL,
boxes: BoxGenerator, bounds: Box, action: ResampleAction];