<> <> <> DIRECTORY ImagerPixelMap, ImagerPixelSeq, FattenPixels, Process ; 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; <> 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.