Introduction
As you know, on Windows Mobile devices there is the possibility of changing the color scheme. In case the application does not use graphic elements, it suffices to use the set of colors provided by the SystemColors class in order for the application to conform to the current scheme. Of the most commonly used, it makes sense to mention ActiveCaption, ActiveCaptionText, InactiveCaption, InactiveCaptionText, WindowText, etc. Also, do not forget about the SystemBrushes class, which presents ready-to-use brushes - there is no need to call constructors, etc.
But what to do when there is a set of images that must match the current color scheme? Is it possible to make a set of pictures for all primary colors?
This is not the best solution - with any changes in the base image, you would have to recreate all versions of the same image in different shades.
')
So what is left? Obviously, it is necessary in some way to transform the base image on the fly. It is known that the main component of the color scheme is contained in the registry at
HKLM \ Software \ Microsoft \ Color , in the DWORD variable
BaseHue . If the value is in the range from 0 to 255, then we have grayscale. From 256 to 510 - the main rainbow :) It has been experimentally established that various topics are often put into this variable "just got", i.e. a value much higher than the range 0..510. As a result, to get an honest BaseHue, we use the following function:
private const String BASEHUE_PATH = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Color" ;
public static int GetBaseHue()
{
object baseHue = Registry.GetValue(BASEHUE_PATH, "BaseHue" , 0);
int bh = baseHue == null ? 0: ( int )baseHue;
if (bh < 255)
return bh;
else
return (bh & 0xFF) + 255;
}
* This source code was highlighted with Source Code Highlighter .
You can read about the real meaning of BaseHue here:
HSL color space .
In short, when the BaseHue value is from 0 to 255, we have grayscale, so the saturation should be 0 (i.e., guaranteed to be a grayscale image). In the case of the range from 256 to 510, the saturation is already at our discretion, as desired. I am satisfied with 255, i.e. maximum color image. Now I will explain, and here is the saturation.
The thing is that the image is stored in the RGB model, and BaseHue has nothing to do with RGB. In the end, it turns out that there is a need to produce RGB -> HSL conversion to obtain the possibility of "coloring" the base image, and then the inverse HSL -> RGB conversion to get the actual colors for the pixels.
Using the checkbox button as an example
So, let us analyze the sequence of actions using the example of a graphic button with a check box and make an active button from an inactive button, and it will be in harmony with the current color scheme. Take a pre-prepared image of an inactive button. I note that the button has transparent zones, they are magenta colors, they can be seen in the corners.
Fig.1 OriginalThe first transformation is simply to make the whole button go to the specified BaseHue (in my case 391).
Fig.2 Conversion doneThat's bad luck, the angular pixels that are responsible for transparency also changed color! Let's go over the image and restore justice (going through the original and finding there transparent pixels):
Fig.3 "Transparent" pixels restoredYes, now everything is fine with transparency, but the box and the box under it are painfully ugly. Add more equity:
Fig.4 Restored checkbox areaThis passage is perhaps not as simple as the previous one. How was this done?
If you look closely, then it is quite obvious that the button image, which is free from the checkbox, is, in general, repeated (except for corner fillets). And exactly the same substrate is under the check-box. What is the conclusion? We have the opportunity to compare the “empty” background with the part where the check-box lies on top of this background and if the difference in R, G or B is more than a certain constant (by a simple search, the number 25 came up), then in the colored picture You can replace a pixel with a pixel from the original.
And here is an example of if you try not to use the threshold, but cut the original “head-on”:
Fig.5. The checkbox zone is restored without taking into account the threshold.Some code
Now about the intricacies of implementation. In Compact Framework, there is not a word about RGB <-> HSL. Googling quickly resolved the conversion issue -
RGB <-> HSL . But not immediately decided the issue with the speed of conversion. As you know, managed code is slow when working with graphics, since GetPixel terribly slow. But for this solution was found. In the MSDN blog about Windows Mobile, I found a great
UnsafeBitmap post for quick pixel manipulation.
Below is a function that rather quickly colors the image at the specified hue, saturation, brigtness, using
UnsafeBitmap :
public static Bitmap ApplyHueSaturation( Bitmap input, int hue, int sat, int brightness)
{
if (input == null )
return null ;
ColorHandler.RGB rgb;
ColorHandler.HSV hsv;
UnsafeBitmap ibmp = new UnsafeBitmap(input);
UnsafeBitmap obmp = new UnsafeBitmap( new Bitmap (input.Width, input.Height));
ibmp.LockBitmap();
obmp.LockBitmap();
for ( int y = 0; y < input.Height; y++)
{
for ( int x = 0; x < input.Width; x++)
{
UnsafeBitmap.PixelData c = ibmp.GetPixel(x, y);
rgb.Red = c.red;
rgb.Blue = c.blue;
rgb.Green = c.green;
hsv = ColorHandler.RGBtoHSV(rgb);
hsv.Hue = hue;
hsv.Saturation = sat;
hsv. value += brightness;
if (hsv. value > 255)
hsv. value = 255;
if (hsv. value < 0)
hsv. value = 0;
ColorHandler.RGB r = ColorHandler.HSVtoRGB(hsv);
obmp.SetPixel(x, y, ( byte )r.Red, ( byte )r.Green, ( byte )r.Blue);
}
}
obmp.UnlockBitmap();
ibmp.UnlockBitmap();
return obmp. Bitmap ;
}
* This source code was highlighted with Source Code Highlighter .
A working example can be downloaded
here .
PS .: The speed of work in reality does not amaze the imagination, however, I do the conversion only once, after which I safely cache, the benefit of the small files are always small, even if it is a whole background for VGA resolution.
PSS .: Yes, when converting to optimize performance, you can certainly immediately ignore transparent pixels to avoid unnecessary sorting through the image. Such steps are shown solely for clarity of the process.
UPD: It is not enough to adapt the images for the current scheme, it is also necessary to display them correctly. Rounded edges can stay with purple corners! :) Read the
continuation of the cycle about working with graphics in the Compact Framework.