<<>> <> <> <> <> <<>> DIRECTORY SF, ViewerOps, Ascii, Commander, CommanderOps, ViewerClasses, Vector2, IO, Basics, Atom, Icons, Imager, Rope, RopeHash, ImagerColor, ImagerSample, RefTab; ColorizeIconsImpl: CEDAR PROGRAM IMPORTS IO, ViewerOps, Commander, CommanderOps, Rope, Ascii, Atom, Imager, RopeHash, ImagerColor, ImagerSample, RefTab ~ BEGIN ROPE: TYPE ~ Rope.ROPE; 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.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] ~ { <> 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]; END. ---------------------------------------------------------------------------------------- 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]; }; TestC: Commander.CommandProc ~ {FOR i: INT IN [0..10) DO Test[i] ENDLOOP}; Commander.Register["Test",TestC];