FattenPixelsImpl:
CEDAR MONITOR
IMPORTS ImagerPixelMap, ImagerPixelSeq, Process
EXPORTS FattenPixels
~ BEGIN
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
PixelSeq:
TYPE ~ ImagerPixelSeq.PixelSeq;
scratchPM: REF ImagerPixelMap.PixelMapRep ← NIL;
killerProcessActive: BOOLEAN ← FALSE;
scratchCondition: CONDITION;
killerDelay: Process.Milliseconds ← 30000;
GetScratch:
ENTRY
PROC
RETURNS [scratch:
REF ImagerPixelMap.PixelMapRep] ~ {
scratch ← scratchPM;
scratchPM ← NIL;
};
ReleaseScratch:
ENTRY
PROC [scratch:
REF ImagerPixelMap.PixelMapRep] ~ {
scratchPM ← scratch;
IF
NOT killerProcessActive
THEN {
TRUSTED{Process.Detach[FORK Killer]};
killerProcessActive ← TRUE;
};
};
Killer:
ENTRY
PROC ~ {
TRUSTED{Process.InitializeCondition[@scratchCondition, Process.MsecToTicks[killerDelay]]};
WAIT scratchCondition;
UNTIL scratchPM # NIL DO WAIT scratchCondition ENDLOOP;
scratchPM ← NIL;
killerProcessActive ← FALSE;
};
FattenSquare:
PUBLIC
PROC [bitmap: PixelMap, brushSize:
NAT] ~ {
start: INTEGER ← 0;
end: INTEGER ← 1;
targetStart: INTEGER ← -(brushSize/2);
targetEnd: INTEGER ← targetStart+brushSize;
WHILE start > targetStart
DO
maxdelta: NAT ← end-start;
newStart: INTEGER ← MAX[start-maxdelta, targetStart];
bitmap.Transfer[bitmap.ShiftMap[start-newStart, 0], [or, null]];
start ← newStart;
ENDLOOP;
WHILE end < targetEnd
DO
maxdelta: NAT ← end-start;
newEnd: INTEGER ← MIN[end+maxdelta, targetEnd];
bitmap.Transfer[bitmap.ShiftMap[end-newEnd, 0], [or, null]];
end ← newEnd;
ENDLOOP;
start ← 0; end ← 1;
WHILE start > targetStart
DO
maxdelta: NAT ← end-start;
newStart: INTEGER ← MAX[start-maxdelta, targetStart];
bitmap.Transfer[bitmap.ShiftMap[0, start-newStart], [or, null]];
start ← newStart;
ENDLOOP;
WHILE end < targetEnd
DO
maxdelta: NAT ← end-start;
newEnd: INTEGER ← MIN[end+maxdelta, targetEnd];
bitmap.Transfer[bitmap.ShiftMap[0, end-newEnd], [or, null]];
end ← newEnd;
ENDLOOP;
};
FattenDiamond:
PUBLIC
PROC [bitmap: PixelMap, brushSize:
NAT] ~ {
alternate: PixelMap ← GetScratch[].Reshape[0, bitmap.Window];
WHILE brushSize > 2
DO
alternate.Clear;
alternate.Transfer[bitmap.ShiftMap[-1, 0], [or, null]];
alternate.Transfer[bitmap.ShiftMap[1, 0], [or, null]];
alternate.Transfer[bitmap.ShiftMap[0, 1], [or, null]];
alternate.Transfer[bitmap.ShiftMap[0, -1], [or, null]];
bitmap.Transfer[alternate, [or, null]];
brushSize ← brushSize - 2;
ENDLOOP;
ReleaseScratch[alternate.refRep];
IF brushSize = 2 THEN FattenSquare[bitmap, 2];
};
FattenRound:
PUBLIC
PROC [bitmap: PixelMap, brushSize:
NAT] ~ {
square: NAT ← brushSize/2;
diamond: NAT ← brushSize-square;
FattenDiamond[bitmap, diamond];
FattenSquare[bitmap, square];
};
ImposeMinimum:
PUBLIC
PROC [bitmap: PixelMap, brushSize:
NAT] ~ {
alternate: PixelMap ← bitmap.Copy[scratch: GetScratch[]];
ImposeMinimumS[alternate, brushSize];
ImposeMinimumF[bitmap, brushSize];
bitmap.Transfer[alternate, [or, null]];
ReleaseScratch[alternate.refRep];
};
ImposeMinimumS:
PUBLIC
PROC [bitmap: PixelMap, brushSize:
NAT] ~ {
bb: DeviceRectangle ~ bitmap.Window;
pixelSeq: PixelSeq ← ImagerPixelSeq.ObtainScratch[bb.sSize+4];
FOR f:
INTEGER
IN [bb.fMin..bb.fMin+bb.fSize)
DO
pixelSeq.LoadS[s: bb.sMin-2, f: f, size: bb.sSize+4, source: bitmap];
ImposeMinimumSeq[pixelSeq, bb.sSize+4, brushSize];
pixelSeq.StoreS[s: bb.sMin-2, f: f, size: bb.sSize+4, dest: bitmap];
ENDLOOP;
ImagerPixelSeq.ReleaseScratch[pixelSeq];
};
ImposeMinimumF:
PUBLIC
PROC [bitmap: PixelMap, brushSize:
NAT] ~ {
bb: DeviceRectangle ~ bitmap.Window;
pixelSeq: PixelSeq ← ImagerPixelSeq.ObtainScratch[bb.fSize+4];
FOR s:
INTEGER
IN [bb.sMin..bb.sMin+bb.sSize)
DO
pixelSeq.LoadF[s: s, f: bb.fMin-2, size: bb.fSize+4, source: bitmap];
ImposeMinimumSeq[pixelSeq, bb.fSize+4, brushSize];
pixelSeq.StoreF[s: s, f: bb.fMin-2, size: bb.fSize+4, dest: bitmap];
ENDLOOP;
ImagerPixelSeq.ReleaseScratch[pixelSeq];
};
ImposeMinimumSeq:
PROC [pixelSeq: PixelSeq, pixelSeqSize:
NAT, brushSize:
NAT] ~ {
runMin, runMax: CARDINAL ← 0;
i: NAT ← 2;
Put a border around the edges to keep the loops simpler.
pixelSeq[0] ← pixelSeq[pixelSeqSize-1] ← 1;
pixelSeq[1] ← pixelSeq[pixelSeqSize-2] ← 0;
WHILE i < pixelSeqSize-2
DO
WHILE pixelSeq[i] = 0 DO i ← i + 1 ENDLOOP;
runMin ← i;
IF i >= pixelSeqSize-2 THEN EXIT;
WHILE pixelSeq[i] # 0 DO i ← i + 1 ENDLOOP;
runMax ← i;
WHILE runMax-runMin < brushSize
DO
IF pixelSeq[runMax] = 0
AND pixelSeq[runMax+1] = 0
THEN {
pixelSeq[runMax] ← 1;
runMax ← runMax + 1;
}
ELSE EXIT;
IF runMax-runMin = brushSize THEN EXIT;
IF pixelSeq[runMin-1] = 0
AND pixelSeq[runMin-2] = 0
THEN {
pixelSeq[runMin-1] ← 1;
runMin ← runMin - 1;
}
ELSE EXIT;
ENDLOOP;
i ← runMax;
ENDLOOP;
};
AddBorder:
PUBLIC
PROC [pixelMap: PixelMap, borderSize:
NAT, borderValue:
CARDINAL]
RETURNS [bordered: PixelMap] ~ {
w: DeviceRectangle ← pixelMap.Window;
w.sMin ← w.sMin - borderSize;
w.fMin ← w.fMin - borderSize;
w.sSize ← w.sSize + 2*borderSize;
w.fSize ← w.fSize + 2*borderSize;
bordered ← ImagerPixelMap.Create[pixelMap.refRep.lgBitsPerPixel, w];
bordered.Fill[w, borderValue];
bordered.Transfer[pixelMap];
};
END.