For disabled icons in Avalonia toolbar, you can go two ways. One is just using an external tool to convert your existing color icons into their color-less variant and have them as a completely separate set. The one I prefer is to actually convert images on demand.
As I'm currently playing with this in Avalonia, I decided to share my code. And it's not as straightforward as I would like. To start with, here is the code:
As I'm currently playing with this in Avalonia, I decided to share my code. And it's not as straightforward as I would like. To start with, here is the code:
public static Bitmap BitmapAsGreyscale(Bitmap bitmap) {
var width = bitmap.PixelSize.Width;
var height = bitmap.PixelSize.Height;
var buffer = new byte[width * height * 4];
var bufferPtr = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try {
var stride = 4 * width;
bitmap.CopyPixels(default, bufferPtr.AddrOfPinnedObject(), buffer.Length, stride);
for (var i = 0; i < buffer.Length; i += 4) {
var b = buffer[i + 0];
var g = buffer[i + 1];
var r = buffer[i + 2];
var grey = byte.CreateSaturating(0.299 * r + 0.587 * g + 0.114 * b);
buffer[i + 0] = grey;
buffer[i + 1] = grey;
buffer[i + 2] = grey;
}
var writableBitmap = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), Avalonia.Platform.PixelFormat.Bgra8888);
using (var stream = writableBitmap.Lock());
Marshal.Copy(buffer, 0, stream.Address, buffer.Length);
return writableBitmap;
} finally {
bufferPtr.Free();
}
}
Since Avalonia doesn't really expose pixel-level operations, first we need to obtain values of all the pixels. The easiest approach I found was just using the CopyPixels
method to get all the data to our buffer. As this code in Avalonia is quite low-level and requires a pointer, we need to have our buffer pinned. Anything pinned also needs releasing, thus our finally
block.
Once we have raw bytes, there is just a matter of figuring out which byte holds which value, and here I suspect that pretty much anybody will use the most common RGBA byte ordering. It's most common by far, and I would say it will be 99% what you end up with.
To get gray, we can use averages, but I prefer using slightly more complicated BT.601 luma calculation. And yes, this doesn't take into account gamma correction; nor is it the only way to get a grayscale. However, I found it works well for icons without much calculation needed. You can opt to use any conversion you prefer as long as the result is a nice 8-bit value. Using this value for each of RGB components gives us the gray component. Further, note that in code above, I only modify RGB values, leaving the alpha channel alone.
Once the bytes are in the desired state, just create a WritableBitmap
based on that same buffer and with the same overall properties (including 32-bit color).