PDInterpBitmapImpl:
PROGRAM
IMPORTS Basics, PrincOpsUtils
EXPORTS PDInterpBitmap
= BEGIN
BitmapDesc: TYPE = PDInterpBitmap.BitmapDesc;
Tile: TYPE = PDInterpBitmap.Tile;
Rectangle: TYPE = PDInterpBitmap.Rectangle;
Function: TYPE = PDInterpBitmap.Function;
bitsPerWord: NAT = Basics.bitsPerWord;
lgBitsPerWord: NAT = Basics.logBitsPerWord;
wordsPerPage: NAT = PrincOps.wordsPerPage;
lgBitsPerPixel: NAT = 0;
pixelsPerWord: NAT = bitsPerWord;
Some inlines for basic bit-hacking operations.
BITAND:
PROC [a, b:
CARDINAL]
RETURNS[
CARDINAL]
= INLINE {RETURN[Basics.BITAND[a, b]]};
Shift:
PROC [a:
CARDINAL, b:
INTEGER]
RETURNS [
CARDINAL]
= INLINE {RETURN[Basics.BITSHIFT[a, b]]};
BITBLT:
PROC [ptr: PrincOps.BBptr] = {
In a separate procedure so the spy can figure out how much time goes here.
Could make this an inline for production.
PrincOpsUtils.BITBLT[ptr];
};
Intersect:
PUBLIC
PROC [a, b: Rectangle]
RETURNS [Rectangle] = {
sMin: INTEGER ← MAX[a.sMin, b.sMin];
fMin: INTEGER ← MAX[a.fMin, b.fMin];
sMax: INTEGER ← MIN[INT[a.sMin]+a.sSize, INT[b.sMin]+b.sSize];
fMax: INTEGER ← MIN[INT[a.fMin]+a.fSize, INT[b.fMin]+b.fSize];
IF sMax <= sMin OR fMax <= fMin THEN RETURN [[0, 0, 0, 0]]
ELSE RETURN [[sMin, fMin, sMax-sMin, fMax-fMin]]
};
Clear:
PUBLIC
PROC [bitmapDesc: BitmapDesc] = {
pointer: LONG POINTER ← bitmapDesc.pointer;
words: LONG CARDINAL ← Basics.LongMult[bitmapDesc.rast, bitmapDesc.lines];
WHILE words > 32768
DO
PrincOpsUtils.LongZero[pointer, 32768];
pointer ← pointer + 32768;
words ← words - 32768;
ENDLOOP;
PrincOpsUtils.LongZero[pointer, words];
};
InsufficientSpace: PUBLIC ERROR = CODE;
Reshape:
PUBLIC
PROC [pointer:
LONG
POINTER, words:
INT, bounds: Rectangle]
RETURNS [bitmapDesc: BitmapDesc] = {
bitsPerPixel: CARDINAL = Shift[1, lgBitsPerPixel];
bitsPerLine:
NAT = Basics.LongMult[bounds.fSize, bitsPerPixel];
Will raise a bounds fault if too many bits per line.
wordsPerLine: CARDINAL = (bitsPerLine+(bitsPerWord-1)) / bitsPerWord;
wordsNeeded: INT = Basics.LongMult[wordsPerLine, bounds.sSize];
IF wordsNeeded > words THEN ERROR InsufficientSpace;
RETURN [[bounds.sMin, bounds.fMin, 0, 0, bounds.sSize, bounds.fSize, pointer, wordsPerLine, bounds.sSize]];
};
ShiftMap:
PUBLIC
PROC [p: BitmapDesc, s, f:
INTEGER]
RETURNS [BitmapDesc] = {
p.sOrigin ← p.sOrigin + s;
p.fOrigin ← p.fOrigin + f;
RETURN [p]
};
ShiftWindow:
PUBLIC
PROC [p: BitmapDesc, s, f:
INTEGER]
RETURNS [BitmapDesc] = {
p.sMin ← p.sMin + s;
p.fMin ← p.fMin + f;
RETURN [p]
};
Clip:
PUBLIC
PROC [p: BitmapDesc, bounds: Rectangle]
RETURNS [BitmapDesc] = {
bounds ← Intersect[Window[p], bounds];
p.sMin ← bounds.sMin-p.sOrigin;
p.fMin ← bounds.fMin-p.fOrigin;
p.sSize ← bounds.sSize;
p.fSize ← bounds.fSize;
RETURN [p]
};
SetWindow:
PUBLIC
PROC [p: BitmapDesc, bounds: Rectangle]
RETURNS [BitmapDesc] = {
RETURN [p]
};
Window:
PUBLIC
PROC [p: BitmapDesc]
RETURNS [Rectangle] = {
RETURN [[p.sOrigin + p.sMin, p.fOrigin + p.fMin, p.sSize, p.fSize]]
};
BufferBounds:
PUBLIC
PROC [p: BitmapDesc]
RETURNS [Rectangle] = {
RETURN [[p.sOrigin, p.fOrigin, p.lines, Shift[p.rast, lgBitsPerWord-lgBitsPerPixel]]]
};
BoundedWindow:
PUBLIC
PROC [p: BitmapDesc]
RETURNS [Rectangle] = {
sMin: INTEGER ← MAX[p.sOrigin + p.sMin, p.sOrigin];
fMin: INTEGER ← MAX[p.fOrigin + p.fMin, p.fOrigin];
sMax: INTEGER ← MIN[p.sOrigin + p.sMin + p.sSize, p.sOrigin + p.lines];
fMax: INTEGER ← MIN[p.fOrigin + p.fMin + p.fSize, p.fOrigin + Shift[p.rast, lgBitsPerWord-lgBitsPerPixel]];
RETURN [[sMin, fMin, MAX[sMax-sMin, 0], MAX[fMax-fMin, 0]]]
};
replicator: ARRAY [0..4] OF CARDINAL = [0FFFFH, 05555H, 01111H, 00101H, 00001H];
Fill:
PUBLIC
PROC [dest: BitmapDesc, area: Rectangle, value:
CARDINAL, function: Function] = {
lgPixelsPerWord: INTEGER = lgBitsPerWord-lgBitsPerPixel;
The following bounding box is calculated in terms of the destination buffer.
sMin: INTEGER = MAX[dest.sMin, 0, area.sMin-dest.sOrigin];
sMax: INTEGER = MIN[dest.sMin+dest.sSize, dest.lines, area.sMin+area.sSize-dest.sOrigin];
fMin: INTEGER = MAX[dest.fMin, 0, area.fMin-dest.fOrigin];
fMax: INTEGER = MIN[dest.fMin+dest.fSize, Shift[dest.rast, lgBitsPerWord-lgBitsPerPixel], area.fMin+area.fSize-dest.fOrigin];
replicatedPixel: CARDINAL ← Basics.BITAND[value, Shift[1, Shift[1, lgBitsPerPixel]]-1] * replicator[lgBitsPerPixel];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ← PrincOpsUtils.AlignedBBTable[@bbTableSpace];
IF sMin<sMax
AND fMin<fMax
THEN {
bb^ ← [
dst: [word: dest.pointer + Basics.LongMult[sMin, dest.rast] + Shift[fMin, -lgPixelsPerWord], bit: Basics.BITAND[fMin, Shift[1, lgPixelsPerWord]-1]],
dstBpl: dest.rast*bitsPerWord,
src: [word: @replicatedPixel, bit: 0],
srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]],
height: sMax-sMin,
width: Shift[fMax-fMin, lgBitsPerPixel],
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE, srcFunc: function.srcFunc, dstFunc: function.dstFunc]
];
BITBLT[bb];
};
};
Transfer:
PUBLIC
PROC [dest, source: BitmapDesc, function: Function] = {
lgPixelsPerWord: INTEGER = lgBitsPerWord-lgBitsPerPixel;
sMin: INTEGER = MAX[dest.sOrigin+MAX[dest.sMin, 0], source.sOrigin+MAX[source.sMin, 0]];
sMax: INTEGER = MIN[dest.sOrigin+MIN[dest.sMin+dest.sSize, dest.lines], source.sOrigin+MIN[source.sMin+source.sSize, source.lines]];
fMin: INTEGER = MAX[dest.fOrigin+MAX[dest.fMin, 0], source.fOrigin+MAX[source.fMin, 0]];
fMax: INTEGER = MIN[dest.fOrigin+MIN[dest.fMin+dest.fSize, Shift[dest.rast, lgBitsPerWord-lgBitsPerPixel]], source.fOrigin+MIN[source.fMin+source.fSize, Shift[source.rast, lgBitsPerWord-lgBitsPerPixel]]];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bbTableSpace];
fMinDest: NAT = fMin - dest.fOrigin;
fMinSource: NAT = fMin - source.fOrigin;
sStartDest: NAT ← sMin-dest.sOrigin;
sStartSource: NAT ← sMin-source.sOrigin;
IF sMin<sMax
AND fMin<fMax
THEN {
bb^ ← [
dstBpl: dest.rast*bitsPerWord,
srcDesc: [srcBpl[source.rast*bitsPerWord]],
height: sMax-sMin,
width: Shift[fMax-fMin, lgBitsPerPixel],
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: function.srcFunc, dstFunc: function.dstFunc]
];
IF source.pointer = dest.pointer
THEN {
sSize: NAT = sMax-sMin;
fSize: NAT = fMax-fMin;
IF (fMinSource+fSize)>fMinDest
AND (fMinDest+fSize)>fMinSource
AND (sStartSource+sSize)>sStartDest
AND (sStartDest+sSize)>sStartSource
THEN {
bb.flags.disjoint ← FALSE; -- the rectangles overlap
IF sStartDest=sStartSource THEN bb.flags.disjointItems ← FALSE; -- so do the items
IF sStartDest>sStartSource
OR (sStartDest=sStartSource
AND fMinDest>fMinSource)
THEN {
-- reverse direction
bb.flags.direction ← backward; bb.srcDesc.srcBpl ← bb.dstBpl ← -bb.dstBpl;
sStartSource ← sStartSource + (sSize-1); sStartDest ← sStartDest + (sSize-1);
};
};
};
bb.dst ← [word: dest.pointer + Basics.LongMult[sStartDest, dest.rast] + Shift[fMinDest, -lgPixelsPerWord], bit: Basics.BITAND[fMinDest, Shift[1, lgPixelsPerWord]-1]];
bb.src ← [word: source.pointer + Basics.LongMult[sStartSource, source.rast] + Shift[fMinSource, -lgPixelsPerWord], bit: Basics.BITAND[fMinSource, Shift[1, lgPixelsPerWord]-1]];
BITBLT[bb];
};
};
IsPowerOfTwo:
PROC [c:
CARDINAL]
RETURNS [
BOOLEAN] =
INLINE {
RETURN [BITAND[c, c-1] = 0]
};
largeBitSize: INT ← 5000;
fSizeHint: NAT ← 100;
CreateTile:
PUBLIC
PROC [rectangle: Rectangle, phase:
INTEGER ← 0, rasterPointer:
LONG
POINTER, scratchPointer:
LONG
POINTER ←
NIL, scratchWords:
INT ← 0]
RETURNS [tile: Tile] = {
ENABLE InsufficientSpace => GO TO InsufficientScratch;
bitWidth: CARDINAL = Shift[rectangle.fSize, lgBitsPerPixel];
bitmapDesc: BitmapDesc = [rectangle.sMin, rectangle.fMin, 0, 0, rectangle.sSize, rectangle.fSize, rasterPointer, (rectangle.fSize+(pixelsPerWord-1))/pixelsPerWord, rectangle.sSize];
IF bitWidth <= 16
AND IsPowerOfTwo[bitWidth]
AND bitmapDesc.sSize < 16
AND phase = 0
THEN {
small: BitmapDesc ← Reshape[scratchPointer, scratchWords, [rectangle.sMin, rectangle.fMin, rectangle.sSize, Shift[16, -lgBitsPerPixel]]];
FOR fShift:
NAT ← 0, fShift+bitmapDesc.fSize
UNTIL fShift>=small.fSize
DO
Transfer[small, ShiftMap[bitmapDesc, 0, fShift], [null, null]];
ENDLOOP;
tile ← [small.sOrigin, small.fOrigin, small.sSize, small.fSize, 0, small.pointer, small.rast, small.lines];
}
ELSE {
fSize: NAT ← (fSizeHint+bitmapDesc.fSize-1)/bitmapDesc.fSize*bitmapDesc.fSize;
bitsPerLine: NAT ← Shift[fSize, lgBitsPerPixel];
sSize: NAT ← MAX[(((largeBitSize+bitsPerLine-1)/bitsPerLine)/bitmapDesc.sSize)*bitmapDesc.sSize, bitmapDesc.sSize];
large: BitmapDesc ← Reshape[scratchPointer, scratchWords, [bitmapDesc.sOrigin+bitmapDesc.sMin, bitmapDesc.fOrigin+bitmapDesc.fMin, sSize, fSize]];
finalPhase: CARDINAL;
WHILE phase<0 DO phase ← phase + bitmapDesc.fSize ENDLOOP;
phase ← phase MOD bitmapDesc.fSize;
finalPhase ← (phase * (sSize/bitmapDesc.sSize)) MOD bitmapDesc.fSize;
large.sSize ← bitmapDesc.sSize;
large.fSize ← bitmapDesc.fSize;
Transfer[large, bitmapDesc, [null, null]];
WHILE large.fSize<fSize
DO
fSizePrev: NAT = large.fSize;
large.fSize ← MIN[fSize, 2*fSizePrev];
Transfer[large, ShiftMap[large, 0, fSizePrev], [null, null]];
ENDLOOP;
WHILE large.sSize<sSize
DO
sSizePrev: NAT = large.sSize;
large.sSize ← MIN[sSize, 2*sSizePrev];
Transfer[large, ShiftMap[large, sSizePrev, phase], [null, null]];
{firstPart: BitmapDesc ← ShiftMap[large, sSizePrev, phase-bitmapDesc.fSize];
firstPart.fSize ← bitmapDesc.fSize;
Transfer[large, firstPart, [null, null]];
};
phase ← 2*phase;
WHILE phase > bitmapDesc.fSize DO phase ← phase - bitmapDesc.fSize ENDLOOP;
ENDLOOP;
tile ← [large.sOrigin, large.fOrigin, sSize, fSize, finalPhase, large.pointer, large.rast, large.lines];
};
EXITS InsufficientScratch => {
tile ← [rectangle.sMin, rectangle.fMin, rectangle.sSize, rectangle.fSize, phase, rasterPointer, (rectangle.fSize+(pixelsPerWord-1))/pixelsPerWord, rectangle.sSize];
};
};
TransferTile:
PUBLIC
PROC [dest: BitmapDesc, tile: Tile, function: Function ← [null, null]] = {
bitWidth: CARDINAL = Shift[tile.fSize, lgBitsPerPixel];
IF bitWidth = bitsPerWord
AND tile.rast = 1
AND tile.sSize <= 16
AND tile.phase = 0
THEN {
startLine: NAT ← MAX[dest.sMin, 0];
endLine: INTEGER ← MIN[dest.sMin+dest.sSize, dest.lines];
startPixel: NAT ← MAX[dest.fMin, 0];
endPixel: INTEGER ← MIN[dest.fMin+dest.fSize, Shift[dest.rast, lgBitsPerWord-lgBitsPerPixel]];
IF endLine>startLine
AND endPixel>startPixel
THEN {
startBit: NAT ← Shift[startPixel, lgBitsPerPixel];
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bbTableSpace];
sOffset: INTEGER ← (dest.sOrigin+dest.sMin-tile.sOrigin) MOD tile.sSize;
fOffset: INTEGER ← (dest.fOrigin+dest.fMin-tile.fOrigin) MOD tile.fSize;
WHILE sOffset < 0 DO sOffset ← sOffset + tile.sSize ENDLOOP;
WHILE fOffset < 0 DO fOffset ← fOffset + tile.fSize ENDLOOP;
bb^ ← [
dst: [word: dest.pointer + Basics.LongMult[startLine, dest.rast] + startBit/bitsPerWord, bit: startBit MOD bitsPerWord],
dstBpl: dest.rast*bitsPerWord,
src: [word: tile.pointer+sOffset, bit: Shift[fOffset, lgBitsPerPixel]],
srcDesc: [gray[[yOffset: sOffset, widthMinusOne: 0, heightMinusOne: tile.sSize-1]]],
height: endLine-startLine,
width: Shift[endPixel-startPixel, lgBitsPerPixel],
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE, srcFunc: function.srcFunc, dstFunc: function.dstFunc]
];
BITBLT[bb];
};
}
ELSE {
sMin: INT ← dest.sOrigin+dest.sMin;
fMin: INT ← dest.fOrigin+dest.fMin;
source: BitmapDesc;
sOrigin: INT ← tile.sOrigin;
fOrigin: INT ← tile.fOrigin;
WHILE sOrigin < sMin
DO
sOrigin ← sOrigin + tile.sSize;
fOrigin ← fOrigin + tile.phase;
ENDLOOP;
WHILE sOrigin > sMin
DO
sOrigin ← sOrigin - tile.sSize;
fOrigin ← fOrigin - tile.phase;
ENDLOOP;
WHILE fOrigin < fMin DO fOrigin ← fOrigin + tile.fSize ENDLOOP;
WHILE fOrigin > fMin DO fOrigin ← fOrigin - tile.fSize ENDLOOP;
source ← [sOrigin, fOrigin, 0, 0, tile.sSize, tile.fSize, tile.pointer, tile.rast, tile.lines];
WHILE source.sOrigin < sMin+dest.sSize
DO
source.fOrigin ← fOrigin;
WHILE source.fOrigin < fMin+dest.fSize
DO
Transfer[dest, source, function];
source.fOrigin ← source.fOrigin + source.fSize;
ENDLOOP;
source.sOrigin ← source.sOrigin + source.sSize;
fOrigin ← fOrigin + tile.phase;
WHILE fOrigin > fMin DO fOrigin ← fOrigin - source.fSize ENDLOOP;
ENDLOOP;
};
};
END.