Getting a Pixel’s Transparency in the Compact Framework

I’m working on a new control and I ran into the need to know if a pixel in an image is transparent or not (essentially I want to ignore clicks on the control if the underlying color is transparent).  My first reaction was that I’d simply call GetPixel on the underlying Bitmap and look at the Color’s A (alpha) value.  Unfortunately, the Compact Framework’s implementation always appears to return 0xff (fully opaque) for teh A channel.


Next I figured I’d call LockBits and look at the pixel data manually.  Once again the CF tried to thwart me because the supported PixelFormat enumeration values in the Compact Framework don’t include anything with Alpha support.


Instead I decided to try to “fool” the framework by creeating my own PixelFormat enumeration that contained all of the actual possible values, not just the three exposed in the CF:


enum PixelFormat
{
    Indexed = 0×00010000, // Indexes into a palette
    GDI = 0×00020000, // Is a GDI-supported format
    Alpha = 0×00040000, // Has an alpha component
    PAlpha = 0×00080000, // Pre-multiplied alpha
    Extended = 0×00100000, // Extended color 16 bits/channel
    Canonical = 0×00200000,

    Undefined = 0,
    DontCare = 0,

    F1bppIndexed = (1 | (1 << 8) | Indexed | GDI),
    F4bppIndexed = (2 | (4 << 8) | Indexed | GDI),
    F8bppIndexed = (3 | (8 << 8) | Indexed | GDI),
    F16bppRGB555 = (5 | (16 << 8) | GDI),
    F16bppRGB565 = (6 | (16 << 8) | GDI),
    F16bppARGB1555 = (7 | (16 << 8) | Alpha | GDI),
    F24bppRGB = (8 | (24 << 8) | GDI),
    F32bppRGB = (9 | (32 << 8) | GDI),
    F32bppARGB = (10 | (32 << 8) | Alpha | GDI | Canonical),
    F32bppPARGB = (11 | (32 << 8) | Alpha | PAlpha | GDI),
    F48bppRGB = (12 | (48 << 8) | Extended),
    F64bppARGB = (13 | (64 << 8) | Alpha | Canonical | Extended),
    F64bppPARGB = (14 | (64 << 8) | Alpha | PAlpha | Extended),
    Max = 15,
}


Since an enum is really just a number, you can directly cast this to a System.Drawing.Imaging.PixelFormat. Nicely (and fortunately), the CF implementation of LockBits doesn’t check to ensure that the value you pass in is one of the defined PixelFormat values and it readly accepts this cast value.  Even more interesting is that it returns a set of data with the proper alpha channel data. 


Gettting the Alpha data, then is as easy as this (I don’t return a Color because there is no easy way to create a Color class that contains Alpha data in the CF either):


private byte GetPixelAlpha(Bitmap image, Point point)
{
    var d = image.LockBits(new Rectangle(0, 0, this.Width, this.Height),
            System.Drawing.Imaging.ImageLockMode.ReadOnly,
            (System.Drawing.Imaging.PixelFormat)PixelFormat.F32bppARGB);

    try
    {
        // the CF’s GetPixel method will always return 0xff (full opaque) for the alpha channel
        // so we have to resort to unsafe shenanigans
        unsafe
        {
            int offset = (d.Width * point.Y * 4) + (point.X * 4);
            byte* p = (byte*)d.Scan0;
            p += offset;

            return *(p + 3);
        }
    }
    finally
    {
        image.UnlockBits(d);
    }
}

2 Comments

  1. Hi,

    This looks cool idea to make desktop code to work on device running CF. Do you mean LockBits does not take into consideration of PixelFormat argument?
    Say for example, if the Bitmap I pass is of 16BPP 565 and use LockBits with PixelsFormat hard-coded as 32BPP, will it work?

    Thanks You
    Sathya Kumar P

Leave a Reply