DIRECTORY
Basics USING [bitsPerWord, RawWords],
CountedVM USING [Handle],
PrincOps USING [BitAddress, SrcFunc, DstFunc];
~
BEGIN
This interface provides most of the functionality of BitBlt, without the attendant risk of smashing the world. 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.
Some of the operations in this interface are documented by code fragments; note that in case of unsuccessful completion (due to bounds fault, for instance), the set of samples moved by the implementation may differ from that implied by the documentation.
SampleMap: TYPE ~ Basics.SampleMap;
SampleMap: TYPE ~ REF SampleMapRep;
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
];
SubMap:
TYPE ~
RECORD [sampleMap: SampleMap, start:
CVEC ← [0,0], size:
CVEC ← lastCVEC];
bitsPerWord:
NAT ~ Basics.bitsPerWord;
CVEC:
TYPE ~
RECORD [s, f:
CARDINAL];
zeroCVEC: CVEC ~ [0, 0];
lastCVEC:
CVEC ~ [
CARDINAL.
LAST,
CARDINAL.
LAST];
Function: TYPE ~ RECORD [dstFunc: PrincOps.DstFunc, srcFunc: PrincOps.SrcFunc];
nullFunction: Function ~ [null, null];
[null, null] makes zero bits white, one bits black
[or, null] makes zero bits transparent, one bits black
[and, null] makes zero bits white, one bits black
[and, complement] makes zero bits transparent, one bits white
[xor, null] makes zero bits transparent, one bits inverted
etcetera.
MultipleReleaseOfScratch: ERROR;
Creation procedures
ComputeWords:
PROC [sSize:
CARDINAL, fSize:
CARDINAL, bitsPerSample: [0..Basics.bitsPerWord]]
RETURNS [
LONG
CARDINAL];
Computes the number of words of storage needed, assuming each scanline is padded to a word boundary.
UnsafeCreate:
UNSAFE
PROC [sSize:
CARDINAL, fSize:
CARDINAL, bitsPerSample: [0..Basics.bitsPerWord], bitsPerLine:
NAT, base: PrincOps.BitAddress, nWords:
LONG
CARDINAL, ref:
REF, 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. nWords is the size of the block in words, used for a consistency check. scratchDescriptor may be provided to avoid the need to allocate a new descriptor.
Create:
PROC [sSize:
CARDINAL, fSize:
CARDINAL, bitsPerSample: [0..Basics.bitsPerWord]]
RETURNS [SampleMap];
Allocates counted storage or counted VM, as needed.
FromVM:
PROC [sSize:
CARDINAL, fSize:
CARDINAL, bitsPerSample: [0..Basics.bitsPerWord], vm: CountedVM.Handle]
RETURNS [SampleMap];
Uses a client-supplied counted VM handle.
Bounds check if unsufficent storage is supplied.
Pads each scanline to a word boundary.
FromSubMap:
PROC [subMap: SubMap]
RETURNS [SampleMap];
Creates a new descriptor on the same sample storage.
Copy:
PROC [subMap: SubMap]
RETURNS [SampleMap];
Allocates a new SampleMap and copies the samples.
ObtainScratch:
PROC [sSize:
CARDINAL, fSize:
CARDINAL, bitsPerSample: [0..Basics.bitsPerWord]]
RETURNS [SampleMap];
ReleaseScratch:
PROC [sampleMap: SampleMap];
DoWithScratch: PROC [sSize: CARDINAL, fSize: CARDINAL, bitsPerSample: [0..Basics.bitsPerWord], action: PROC[SampleMap]];
Operations
Size:
PROC [sampleMap: SampleMap]
RETURNS [
CVEC];
Clear:
PROC [sampleMap: SampleMap];
A fast way to clear a sampleMap 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.
GetSample:
PROC [sampleMap: SampleMap, index:
CVEC]
RETURNS [
CARDINAL];
PutSample:
PROC [sampleMap: SampleMap, index:
CVEC, value:
CARDINAL, function: Function ← nullFunction];
Stores modified value into the SampleMap at indexed location, dropping extra high-order bits.
Bounds fault if index is out of range.
Transfer:
PROC [dest: SampleMap, destStart:
CVEC ← zeroCVEC, source: SubMap, function: Function ← nullFunction];
size ← MIN[source.size, Size[dest]-destStart, Size[source.sampleMap]-source.start];
FOR s: CARDINAL IN [0..size.s) DO
FOR f: NAT IN [0..size.f) DO
sample: CARDINAL ~ GetSample[source.sampleMap, [source.start.s+s, source.start.f+f]];
PutSample[dest, [destStart.s+s, destStart.f+f], sample, function];
ENDLOOP;
ENDLOOP;
Move:
PROC [sampleMap: SampleMap, destStart:
CVEC ← zeroCVEC, sourceStart:
CVEC ← zeroCVEC, size:
CVEC ← lastCVEC, function: Function ← nullFunction];
Transfers a rectangular region of the sampleMap, ordering it so ripple does not occur even if the rectangles overlap.
Fill:
PROC [dest: SubMap, value:
CARDINAL, function: Function ← nullFunction];
TileFromStipple:
PROC [stipple:
CARDINAL, bitsPerSample: [0..Basics.bitsPerWord] ← 1, sample0:
CARDINAL ← 0, sample1:
CARDINAL ← 1, obtainScratch:
BOOL ←
FALSE]
RETURNS [SampleMap];
Makes a tile from a 4-by-4 pattern.
If obtainScratch, gets space with ObtainScratch and client should call ReleaseScratch.
TileBox:
PROC [dest: SampleMap, start:
CVEC, size:
CVEC, source: SampleMap, s0, f0:
INTEGER, phase:
NAT, function: Function ← nullFunction];
Tiles the rectangle designated by (start, size) with the tile designated by (source, s0, f0, phase).
start, size, s0, and f0 all work in the dest coordinate system.
The DIV and MOD functions in the following take the sign of the remainder equal to the sign of the divisor, rather than of the dividend as is usual.
FOR s: NAT IN [start.s..start.s+size.s) DO
FOR f: NAT IN [start.f..start.f+size.f) DO
sSource: CARDINAL ~ (s-s0) MOD source.sSize;
fSource: CARDINAL ~ (f-f0 - ((s-s0)/source.sSize)*phase) MOD source.fSize;
sample: CARDINAL ~ GetSample[source, [sSource, fSource]];
PutSample[dest, [s, f], sample, function];
ENDLOOP;
ENDLOOP;
BoxProc: TYPE ~ PROC [smin, fmin, smax, fmax: CARDINAL];
TileBoxes:
PROC [dest: SampleMap, boxes:
PROC[BoxProc], source: SampleMap, s0, f0:
INTEGER, phase:
NAT, function: Function ← nullFunction];
Effectively calls TileBox for each of the boxes.
Trim:
PROC [subMap: SubMap, background:
CARDINAL ← 0]
RETURNS [SubMap];
For removing a constant border.
All of the pixels in subMap not in the result will be equal to background.
Client must keep track of the origin.
Sample Buffers
When performing operations on many contiguous samples, it is usually a good idea to unpack them into a one-word-per-sample buffer, work on them, and pack them up again into the output. This works well because whole-word access is typically much faster than partial word access (although byte access is not too bad). The following operations will help to do this kind of processing.
Buffer: TYPE ~ REF BufferRep;
BufferRep:
TYPE ~
RECORD [length:
NAT, samples:
SEQUENCE maxLength:
NAT
OF
CARDINAL];
Apply:
PROC [dest, source: SampleMap, op:
PROC [a, b:
CARDINAL]
RETURNS [
CARDINAL]];
This is here primarily as an example of how to use the other procedures in this section; look at the implementation.
The semantics are:
FOR s: NAT IN [0..MIN[dest.sSize, source.sSize]) DO
FOR f: NAT IN [0..MIN[dest.fSize, source.fSize]) DO
sample: CARDINAL ~ op[GetSample[dest, [s, f]], GetSample[source, [s, f]]];
PutSample[dest, [s, f], sample];
ENDLOOP;
ENDLOOP;
maxCount:
NAT ~
LAST[
NAT];
Get:
PROC [buffer: Buffer, start:
NAT ← 0, count:
NAT ← maxCount,
sampleMap: SampleMap, s, f:
NAT ← 0, ds:
NAT ← 0, df:
NAT ← 1];
FOR j: NAT IN [0..MIN[count, NAT[buffer.length-start]]) DO
buffer[start+j] ← GetSample[sampleMap, [s+j*ds, f+j*df]];
ENDLOOP;
Put:
PROC [buffer: Buffer, start:
NAT ← 0, count:
NAT ← maxCount,
sampleMap: SampleMap, s, f:
NAT ← 0, ds:
NAT ← 0, df:
NAT ← 1,
function: Function ← nullFunction];
FOR j: NAT IN [0..MIN[count, NAT[buffer.length-start]]) DO
PutSample[sampleMap, [s+j*ds, f+j*df], buffer[start+j], function];
ENDLOOP;
Flip:
PROC [buffer: Buffer];
Reverses the order of the samples in the buffer. Useful for changing scan order.
FOR j: NAT IN [0..buffer.length/2) DO
t: CARDINAL ← buffer[j];
buffer[j] ← buffer[buffer.length-1-j];
buffer[buffer.length-1-j] ← t;
ENDLOOP;
ObtainBuffer:
PROC [length:
NAT]
RETURNS [Buffer];
Resulting buffer has buffer.length = length and buffer.maxLength >= length.
ReleaseBuffer:
PROC [buffer: Buffer];
GetPointer:
PROC [buffer: Buffer, start:
NAT, count:
NAT]
RETURNS [
LONG
POINTER
TO Basics.RawWords];
Returns a long pointer to buffer[start], after making sure start and start+count do not exceed buffer.maxLength