A few days ago I was writing a simple arcade in Delphi 7 and I needed to reflect a cooldown
of abilities when they've been cast and are now recharging. Most common approach used, for
example, in Warcraft 3 is to dispaly ability's icon as if it was a clock display and gray
out the area which stands for «remaining time».
The small screenshot on the left should demonstrate you what I mean.
And I decided to go for this approach as well and for this I needed to somehow determine where «the grayed out» part of the icon should be. The solution I found is actually pretty straightforward:
Another question is how do we convert an RGB color into its corresponding grayscale alternative – my quick googling resulted in a simple formula like this one: RED * 0.3 + GREEN * 0.59 + BLUE * 0.11. The result you get will represent grayscale version of any full-color pixel.
Now without further ado let us carry on for the functions themselves.
The following 2 functions can be used to convert an RGB PNG image into its grayscale version:
pascal{ Returns grayscale version of a color. } function RgbToGray(C: TColor): TColor; begin C := Round(C and $FF * 0.3 + C and $00FF00 shr 8 * 0.59 + C shr 16 * 0.11); Result := RGB(C, C, C); end; { Converts whole PNG image to grayscale. } procedure ToGrayscale(PNG: TPNGObject); var X, Y: Integer; begin for X := 0 to PNG.Width - 1 do for Y := 0 to PNG.Height - 1 do PNG.Pixels[X, Y] := RgbToGray(PNG.Pixels[X, Y]); end;
And this is the function that can be used to create effect shown on the screenshot on the top. Its parameters are:
pascalprocedure GrayscaleClock(PNG: TPNGObject; Percent: SmallInt; Clockwise: Boolean = True; Inverse: Boolean = True); var Angle: Extended; Mask: TBitMap; X, Y: Integer; Color: TColor; begin Mask := TBitmap.Create; try Mask.Width := PNG.Width; Mask.Height := PNG.Height; Mask.Canvas.Brush.Color := clWhite; Mask.Canvas.FillRect( Rect(0, 0, Mask.Width, Mask.Height) ); SetArcDirection(Mask.Canvas.Handle, AD_CLOCKWISE); if not Clockwise then Percent := -1 * Percent; Angle := (50 - Percent) / 100.0 * 2.0 * 3.1416; // rad to grad. with PNG do Mask.Canvas.Pie(0, 0, Width, Height, Width div 2, 0, Round(Width div 2 + Width div 2 * Sin(Angle)), Round(Height div 2 + Height div 2 * Cos(Angle))); Mask.Canvas.Brush.Color := clBlack; Mask.Canvas.FloodFill(PNG.Width div 2, 2, clBlack, fsBorder); Color := Sign(Byte(Inverse)) * clWhite; with PNG do for X := 0 to Width - 1 do for Y := 0 to Height - 1 do if Mask.Canvas.Pixels[X, Y] = Color then PNG.Pixels[X, Y] := RgbToGray(PNG.Pixels[X, Y]); finally Mask.Free; end; end;
Note: in the code above you can change the starting point for 0% – this code:
pascal50 - Percent
sets it to be on top-middle point.
In my previous article about PNG
transparency I attached a descendant of standard TSpeedButton Delphi control which supported
PNG glyphs and also a neat feature of combining 1, 2 or 3 of them together and show as glyph.
The version I attach here is further enchanced:
Download enchanced TPNGSpeedButton component (it has no external dependencies except for TPNGObject itself).
If you haven't installed any custom component yet simply go to Component → Install component menu. Note that this class was written and tested on Delphi of 7th version only.