ImagerSample.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, December 4, 1985 11:31:32 am PST
Doug Wyatt, March 7, 1986 2:41:21 pm PST
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];
ImagerSample: CEDAR DEFINITIONS
~ 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];
END.