~
BEGIN
RasterSampleMap: TYPE ~ ImagerSample.RasterSampleMap;
SampleBuffer:
TYPE ~ ImagerSample.SampleBuffer;
ColorizeIconProc: TYPE = PROC [context: Imager.Context, label: Rope.ROPE, iconInfo: Icons.IconRef, x, y: INTEGER] RETURNS [Icons.IconRef];
HSVColor:
PROC [H, S, V:
REAL]
RETURNS [Imager.Color] ~ {
RETURN [ImagerColor.ColorFromHSV[H, S, V]]
};
bg1:
ARRAY [0..8)
OF Imager.Color ¬ [
HSVColor[0.09, 0.5, 1],
HSVColor[0.17, 0.4, 1],
HSVColor[0.30, 0.5, 1],
HSVColor[0.50, 0.4, 1],
HSVColor[0.60, 0.4, 1],
HSVColor[0.66, 0.4, 1],
HSVColor[0.80, 0.4, 1],
HSVColor[0.94, 0.4, 1]
];
bg2:
ARRAY [0..6)
OF Imager.Color ¬ [
HSVColor[0/6.0, 0.4, 1],
HSVColor[1/6.0, 0.4, 1],
HSVColor[2/6.0, 0.4, 1],
HSVColor[3/6.0, 0.4, 1],
HSVColor[4/6.0, 0.4, 1],
HSVColor[5/6.0, 0.4, 1]
];
debugStream: IO.STREAM ¬ NIL;
LabelHash:
PROC [label:
ROPE]
RETURNS [
CARD] ~ {
start: INT ¬ MAX[Rope.FindBackward[s1: label, s2: "/"], 0];
end: INT ¬ Rope.SkipTo[s: label, pos: start, skip: " .!:"];
IF end-start < 3 THEN { start ¬ 0; end ¬ INT.LAST };
WITH debugStream
SELECT
FROM
stream: IO.STREAM => { IO.PutRope[stream, label, start, end-start]; IO.PutChar[stream, '\n] };
ENDCASE;
RETURN [RopeHash.FromRope[rope: label, case: FALSE, start: start, len: end-start]]
};
ColorizeIcon: ColorizeIconProc ~ {
BEGIN ENABLE
UNCAUGHT =>
CONTINUE;
sep: Separations ~ GetSeparations[iconInfo];
IF sep #
NIL
THEN {
IF use1
THEN
Imager.SetColor[context, bg1[LabelHash[label]
MOD
LENGTH[bg1]]]
ELSE
Imager.SetColor[context, bg2[LabelHash[label] MOD LENGTH[bg2]]];
Imager.MaskRectangleI[context, x+iconInfo.lx, y+iconInfo.ly, iconInfo.lw, iconInfo.lh];
Imager.MaskBitmap[context, sep.background, [Icons.iconH,0], Imager.defaultScanMode, [x,y]];
Imager.SetColor[context, ImagerColor.ColorFromRGB[[0,0,.5]]];
};
END;
RETURN [iconInfo]
};
cache: RefTab.Ref ¬ RefTab.Create[];
GetSeparations:
PROC [iconInfo: Icons.IconRef]
RETURNS [sep: Separations ¬
NIL] ~ {
Updater: RefTab.UpdateAction ~ {
IF found
THEN sep ¬ NARROW[val]
ELSE {
sep ¬ ComputeSeparations[iconInfo];
RETURN [op: store, new: sep]
};
};
RefTab.Update[cache, iconInfo, Updater];
};
Separations: TYPE ~ REF SeparationsRep;
SeparationsRep:
TYPE ~
RECORD [
background: RasterSampleMap
];
ComputeSeparations:
PROC [iconInfo: Icons.IconRef]
RETURNS [Separations] ~ {
Link: TYPE = [0..Icons.iconH*Icons.iconW];
LinkArray: TYPE = PACKED ARRAY [0..Icons.iconH*Icons.iconW] OF Link;
null: Link ~ Icons.iconH*Icons.iconW;
link: REF LinkArray = NEW[LinkArray ¬ ALL[null]];
ndx:
PROC [i: [0..Icons.iconH), j: [0..Icons.iconW)]
RETURNS [Link] ~
INLINE {
RETURN [i*Icons.iconW+j]
};
Rep:
PROC [a: Link]
RETURNS [Link] ~ {
r: Link ¬ a;
UNTIL link[r] = null DO r ¬ link[r] ENDLOOP;
UNTIL a = r
DO
next: Link ¬ r;
link[a] ¬ r;
a ¬ next;
ENDLOOP;
RETURN [r]
};
MakeEquiv:
PROC [a, b: Link] ~ {
aRep: Link ¬ Rep[a];
bRep: Link ¬ Rep[b];
IF aRep # bRep THEN link[aRep] ¬ bRep;
};
ScanRegion:
PROC [a: Link]
RETURNS [ImagerSample.RasterSampleMap] ~ {
r: Link ~ Rep[a];
map: ImagerSample.RasterSampleMap ¬ ImagerSample.NewSampleMap[[max:[Icons.iconH,Icons.iconW]]];
ImagerSample.Clear[map];
FOR i: [0..Icons.iconH)
IN [0..Icons.iconH)
DO
FOR j: [0..Icons.iconW)
IN [0..Icons.iconW)
DO
IF Rep[ndx[i,j]] = r
THEN {
ImagerSample.Put[map, [i,j], 1];
};
ENDLOOP;
ENDLOOP;
RETURN [map]
};
bits: PACKED ARRAY [0..Icons.iconH*Icons.iconW) OF [0..1] ~ LOOPHOLE[iconInfo.bits];
FOR i: [0..Icons.iconH)
IN [0..Icons.iconH)
DO
FOR j: [0..Icons.iconW)
IN [0..Icons.iconW)
DO
IF bits[ndx[i,j]] = 0
THEN {
IF i # 0 AND bits[ndx[i-1,j]] = 0 THEN MakeEquiv[ndx[i-1,j],ndx[i,j]];
IF j # 0 AND bits[ndx[i,j-1]] = 0 THEN MakeEquiv[ndx[i,j],ndx[i,j-1]];
};
ENDLOOP;
ENDLOOP;
RETURN [NEW[SeparationsRep ¬ [background: ScanRegion[ndx[Icons.iconH-1-(iconInfo.ly+iconInfo.lh/2), iconInfo.lx+iconInfo.lw/2]]]]];
};
FindRegion:
PROC [bitmap: ImagerSample.SampleMap, s, f:
INTEGER]
RETURNS [result: RasterSampleMap] ~ {
box: SF.Box ~ bitmap.GetBox;
fSize: NAT ~ box.max.f-box.min.f;
link: RasterSampleMap ~ ImagerSample.ObtainScratchMap[box, 32];
Link: TYPE ~ MACHINE DEPENDENT RECORD [s, f: INT16];
null: Link ~ [INT16.FIRST, 0];
p: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[fSize];
q: SampleBuffer ¬ ImagerSample.ObtainScratchSamples[fSize];
GetLink:
PROC [a: Link]
RETURNS [Link] ~ {
RETURN [LOOPHOLE[ImagerSample.Get[link, [a.s, a.f]]]]
};
PutLink:
PROC [a, r: Link] ~ {
ImagerSample.Put[link, [a.s, a.f], LOOPHOLE[r]];
};
Rep:
PROC [a: Link]
RETURNS [Link] ~ {
Finds the representative for the link (union-find with path compression)
r: Link ¬ a;
next: Link;
UNTIL (next ¬ GetLink[r]) = null DO r ¬ next ENDLOOP;
UNTIL a = r
DO
next ¬ r;
PutLink[a, r];
a ¬ next;
ENDLOOP;
RETURN [r]
};
MakeEquiv:
PROC [a, b: Link] ~ {
aRep: Link ¬ Rep[a];
bRep: Link ¬ Rep[b];
IF aRep # bRep THEN PutLink[aRep, bRep];
};
ScanRegion:
PROC [a: Link]
RETURNS [ImagerSample.RasterSampleMap] ~ {
r: Link ~ Rep[a];
map: ImagerSample.RasterSampleMap ¬ ImagerSample.NewSampleMap[box];
ImagerSample.Clear[map];
FOR s:
INT
IN [box.min.s..box.max.s)
DO
FOR f:
INT
IN [box.min.f..box.max.f)
DO
IF Rep[[s,f]] = r THEN ImagerSample.Put[map, [s, f], 1];
ENDLOOP;
ENDLOOP;
RETURN [map]
};
ImagerSample.Fill[map: link, value: LOOPHOLE[null]];
FOR s:
INT
IN [box.min.s..box.max.s)
DO
ImagerSample.GetSamples[map: bitmap, initIndex: [s, box.min.f], buffer: q];
FOR j:
INT
IN [0..fSize)
DO
f: INT16 ~ box.min.f+j;
IF s#box.min.s AND q[j]#p[j] THEN MakeEquiv[[s-1,f],[s,f]];
IF j#0 AND q[j]#q[j-1] THEN MakeEquiv[[s,f],[s,f-1]];
ENDLOOP;
{t: SampleBuffer ¬ p; p ¬ q; q ¬ t}
ENDLOOP;
result ¬ ScanRegion[[s,f]];
ImagerSample.ReleaseScratchMap[link];
ImagerSample.ReleaseScratchSamples[p];
ImagerSample.ReleaseScratchSamples[q];
};
PaintIfIconic:
PROC [v: ViewerClasses.Viewer]
RETURNS [
BOOL ¬
TRUE] ~ {
IF v.iconic THEN ViewerOps.PaintViewer[v, all];
};
Register:
PROC [ref:
REF ColorizeIconProc] ~ {
Atom.PutProp[$Icons, $ColorizeIcon, ref];
};
use1: BOOL ¬ TRUE;
iconColorOn: BOOL ¬ FALSE;
docIconColor: Rope.ROPE ~ "Control icon colorization. Args: on | off";
Lower: Rope.TranslatorType ~ {RETURN [Ascii.Lower[old]]};
IconColorCommand: Commander.CommandProc ~ {
arg: ROPE ~ CommanderOps.NextArgument[cmd];
atom: ATOM ~ IF arg = NIL THEN NIL ELSE Atom.MakeAtom[arg.Translate[translator: Lower]];
SELECT atom
FROM
$on, $one => { Register[NEW[ColorizeIconProc ¬ ColorizeIcon]]; use1 ¬ TRUE; };
$two => { Register[NEW[ColorizeIconProc ¬ ColorizeIcon]]; use1 ¬ FALSE; };
$off => { Register[NIL]; RefTab.Erase[cache] };
ENDCASE => ERROR CommanderOps.Failed[cmd.procData.doc];
ViewerOps.EnumerateViewers[PaintIfIconic];
};
Commander.Register["IconColor", IconColorCommand, docIconColor];
----------------------------------------------------------------------------------------
Test:
PROC [n:
INT] ~ {
IconFileFormat: TYPE = Icons.IconFileFormat;
format: REF IconFileFormat ~ NEW[IconFileFormat]; -- 1024 bytes!
bytesPerIcon: INT ~ BYTES[IconFileFormat];
iconInfo: Icons.IconRef;
stream: IO.STREAM ¬ ImagerSys.OpenInputFile["/r/Standard.icons"];
IO.SetIndex[stream, bytesPerIcon*n];
TRUSTED {
base: LONG POINTER TO Basics.RawBytes ~ LOOPHOLE[format];
bytesRead: INT ~ IO.UnsafeGetBlock[stream, [base: base, count: bytesPerIcon]];
};
iconInfo ¬ NEW[Icons.IconRep ¬ [bits: format.bits,
label: format.label, invertLabel: format.invertLabel,
lx: format.lx, ly: format.ly, lw: format.lw, lh: format.lh]];
IO.Close[stream];
BitmapViewer.SetBitmap[BitmapViewer.Create[], ComputeSeparations[iconInfo].background];
};