Graying out a pie piece in PNG image
  1. 1. The functions
    1. 1.1. «Clock graying»
  2. 2. Enchanced speed button – TPNGSpeedButton

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:

  1. First we draw on an empty surface a pie piece using standard Pie WinAPI function (encapsulated in TCanvas.Pie;
  2. Then we flood fill it with some other color;
  3. And now we have a map of pixels we can use to determine the grayed and normal parts of the image. We simply go thru all 0..X and 0..Y, checking for pixel color on that map.

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 functions

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;

«Clock graying»

http://proger.i-forge.net/Мои проги/Delphi/Graying out a pie piece/samples.png

Samples of what you'll get with different combinations of Clockwise and Inverse.

And this is the function that can be used to create effect shown on the screenshot on the top. Its parameters are:

PNG
The PNG image you want to clock-grayscale;
Percent
A number between -100 and +100 (inclusive) representing the amount of gray area (that «remaining clock piece»);
Clockwise
Graying can happen either clockwise or counterclockwise;
Inverse
Determines what area to gray out: the one that lies within Percent or the one outside of it.
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.

Enchanced speed button – TPNGSpeedButton

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.