Debugger Visualizers introduced in RAD Studio 2010, for both Delphi 2010 and C++Builder 2010, are one of the most interesting new IDE features. On the ITDevCon 2009 Delphi conference in Verona I have met Boian Mitov from Mitov Software, who actually wrote a sample "TBitmap" visualizer for Delphi 2010. Boian has architected lots of VCL components for all kinds of signal processing, including advanced video and audio processing implementation. Very cool and sophisticated stuff demonstrating the power of Delphi:-)
OK. I have downloaded and installed TBitmap visualizer into the IDE and it would be good to actually debug some code to try it out. So I thought about implementing an algorithm to convert arbitrary bitmaps to grayscale.
Colors in computing are typically represented by the combination of intensity of the main three colors: red, green and blue (RGB). There are also other representations possible, but let’s stick to RGB. The color is considered gray when intensities for all three basic colors are the same. The special cases of gray are: black with zero intensities, and white with maximum possible intensities of all three colors.
One of the most popular color format is 24bit, where each color is represented by one byte, or a value from 0 to 255 range. The additional, fourth byte, is used to store the Alpha channel or - in other words - transparency level.
Few clicks away I have found the description of three algorithms to convert to grayscale as implemented by GIMP: lightness, average and luminosity. All these formulas take R, G and B values of the color and calculate a value in grayscale that can be assigned back to the original R, G and B parts yielding a gray color.
The Lightness method averages the most prominent and least prominent colors: (max(R, G, B) + min(R, G, B)) div 2.
The Average is simple average value of all three colors: (R + G + B) div 3.
The formula for Luminosity is 0.21 R + 0.71 G + 0.07 B. It is a weighted average to account for human perception. We’re more sensitive to green than other colors, so green is weighted most heavily.
That’s a playing field for the TBitmap visualizer. You can see the contents of the bitmap before and after converting an image to a grayscale. You only need to set a breakpoint and select "Show Bitmap" in the debugger local variables "Visualizers" context menu. The visualizer displays the bitmap itself and information about its size, format and the color of a pixel if a mouse hovers on the bitmap.
Here is the actual source code for converting bitmaps to grayscale. I am sure this could be optimized for speed, but it is more for illustration purposes.
unit uBitmapUtils; interface uses Graphics, Windows; type TColor2Grayscale = ( c2gLightness, c2gAverage, c2gLuminosity ); procedure ToGray(aBitmap: Graphics.TBitmap; cg: TColor2Grayscale = c2gLuminosity); function ColorToGray(c: TColor; cg: TColor2Grayscale = c2gLuminosity): TColor; implementation uses SysUtils, Math; function Min(const A, B, C: integer): integer; begin Result := Math.Min(A, Math.Min(B,C)); end; function Max(const A, B, C: integer): integer; begin Result := Math.Max(A, Math.Max(B,C)); end; function ColorToGray(c: TColor; cg: TColor2Grayscale = c2gLuminosity): TColor; var R, G, B, X : Integer; begin R := c and $ff; G := (c and $ff00) shr 8; B := (c and $ff0000) shr 16; case cg of c2gLightness: X := (max(R, G, B) + min(R, G, B)) div 2; c2gAverage: X := (R + G + B) div 3; c2gLuminosity: X := round(0.21*R + 0.71*G + 0.07*B); else raise Exception.Create('Unknown Color2Grayscale value'); end; Result := TColor(RGB(X,X,X)); end; procedure ToGray(aBitmap: Graphics.TBitmap; cg: TColor2Grayscale = c2gLuminosity); var i, j: integer; begin if aBitmap <> nil then begin for i := 0 to aBitmap.Width - 1 do for j := 0 to aBitmap.Height - 1 do aBitmap.Canvas.Pixels[i,j] := ColorToGray(aBitmap.Canvas.Pixels[i,j], cg); end; end; end.