Wednesday, July 28, 2010

Since I use the OpenNETCF.IoC framework in just about every project I do any more, you can imagine I've created a pretty good collection of Modules and Services for common business problems I run into.  I've decided that I'm going to start putting some of these out in the public domain along with the framework and my first release is the OpenNETCF.Location module.  It's basically a module that prvides a GPS location service (GpsService to be exact). 

The default implementation is wrapper around the GPS intermediate driver, but it's not using the CF classes that Microsoft ships with the WinMo SDKs.  I looked at Microsoft's implementation and it felt really ugly to me, so I rewrote it the way it should have been done in the first place.  I don't yet have a simple sample application extracted for it (I have apps that use it, but they're too complex for a sample), but I'll add one as time permits.

The code is part of the latest change set (49823) - it's not yet in the release download - so if you want it, get it off of the Source Code page.

7/28/2010 12:45:25 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 

In my continuing effort to get code that I have lying around out to where it's publicly available and easy to find, I've pushed my old FTP library up to Codeplex.

7/28/2010 11:45:16 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Friday, July 23, 2010

I've been working on a new control this week that depends a whole lot on transparency and the alpha channel.  The Compact Framework's Bitmap class is actually really, really bad and it's made for all sorts of swearing. 

The design of the control requires me to regularly create a "transparent" background.  We know that you can't create a transparent bitmap directly (which is the fundamental thing that is killing me) so I end up using a TransparentClearBitmap method that first appeared in Project Resistance that does a LockBits and walks the image setting transparency.  This is slow (600ms for my bitmap on the emulator).

I needed to draw on top of this Bitmap to assemble my control layout. The issue is that every time I repainted the control due to a change, I need a new transparent Bitmap and had to pay the 600ms piper.  Needless to say this gave for a really bad user experience.  Well how about just caching the transparent bitmap and returning a copy of it when necessary?  That should be simple, right?

I tried to use the Bitmap constructor to make a copy:

var bmp = new Bitmap(m_transparentBitmap);

Of course it failed. The new Bitmap lost all transparency data.

Well maybe the Clone method would do what I want, after all that's what I needed - an exact clone:

var bmp = m_transparentBitmap.Clone;

Again, all transparency data was lost. Joy.

So it was off to the drawing board to re-implement what should have already been working.  Here's the result - a TransparentBitmap that you can clone and actually keep the transparency info:

public class TransparentBitmap
{
    private Bitmap m_bmp;

    public int Width { get; private set; }
    public int Height { get; private set; }

    public TransparentBitmap(int width, int height)
    {
        Width = width;
        Height = height;
        m_bmp = new Bitmap(width, height, (System.Drawing.Imaging.PixelFormat)PixelFormat.F32bppARGB);

        GraphicTools.TransparentClearBitmap(m_bmp);
    }

    private TransparentBitmap(Bitmap image)
    {
        m_bmp = image;
        Width = image.Width;
        Height = image.Height;
    }

    public static implicit operator Bitmap(TransparentBitmap t)
    {
        return t.m_bmp;
    }

    public TransparentBitmap Clone()
    {
        var clone = new Bitmap(Width, Height, (System.Drawing.Imaging.PixelFormat)PixelFormat.F32bppARGB);

        var dest = clone.LockBits(
                new Rectangle(0, 0, Width, Height),
                ImageLockMode.WriteOnly,
                (System.Drawing.Imaging.PixelFormat)PixelFormat.F32bppARGB);

        try
        {
            var src = m_bmp.LockBits(
                    new Rectangle(0, 0, Width, Height),
                    ImageLockMode.ReadOnly,
                    (System.Drawing.Imaging.PixelFormat)PixelFormat.F32bppARGB);

            try
            {
                int bytes = src.Width * src.Height * 4;
                memcpy(dest.Scan0, src.Scan0, bytes);
            }
            finally
            {
                m_bmp.UnlockBits(src);
            }
        }
        finally
        {
            clone.UnlockBits(dest);
        }

        return new TransparentBitmap(clone);
    }

    [DllImport("coredll.dll", SetLastError = true)]
    private static extern IntPtr memcpy(IntPtr to, IntPtr from, int count);
}

The Clone takes only about 4ms to run instead of 600ms to create new, so it certainly improved things.

7/23/2010 5:02:06 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Wednesday, July 21, 2010

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 = 0x00010000, // Indexes into a palette
    GDI = 0x00020000, // Is a GDI-supported format
    Alpha = 0x00040000, // Has an alpha component
    PAlpha = 0x00080000, // Pre-multiplied alpha
    Extended = 0x00100000, // Extended color 16 bits/channel
    Canonical = 0x00200000,

    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);
    }
}

7/21/2010 4:50:57 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Tuesday, July 20, 2010

Codeplex recently updated the server hosting the IoC project to TFS10.  Since we still have to use Studio 2008 for device development, I had to do some client changes for bindings to get to the server.  TFS 2010 added the concept of a "Team Project Collection", which Codeplex is using - but Studio 2008's Team Explorer dialog has no provision for it.  It took me a while with a search engine to figure out how to actually attach - basically you have to manually type the full path into the connection dialog in Team Explorer like so:

http://<serverName>:<port>/<vdir>/<collectionName>

So for the new TFS10 on Codeplex, it looks like this:

https://tfs.codeplex.com:443/tfs/tfs10

7/20/2010 11:46:45 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Monday, July 12, 2010

One drawback to the OpenNETCF.IoC framework was the difficulty in making object generation loosely coupled.  This is especially true when object types you want to create change depending on your runtime environment.  For example let's say we have an interface IAccelerometerService.  Different devices have different implementations, and the emulator and your integration tests don't even have hardware so they need a simulator.  So you create some classes that derive from the interface like this:

public interface IAccelerometerService
{
}

public class DeviceAAccelerometer : IAccelerometerService
{
// handles the accelerometer by using APIs for device A
}

public class DeviceBAccelerometer : IAccelerometerService
{
// handles the accelerometer by using APIs for device B
}

public class SimulatedAccelerometer : IAccelerometerService
{
// simulates an accelerometer (emulator, test use, etc)
}

That's all well and good, but let's say we have a generic infrastructure module that does the object creation and injection.  We don't want it to have to know about the concrete class types. They might be in separate assemblies that may not even exist - you certainly don't need (or want) to ship emulator implementation for your hardware. 

How would you handle this? The IoC framework doesn't like the following construct:

RootWorkItem.Items.AddNew<IAccelerometerService>();

Because it has no idea what concrete type you want it to create.  In this case it will throw an IOCException.

Today I added a "registration" process to the framework.  You now can do the following:

RootWorkItem.RegisterType(typeof(SimulatedAccelerometer), typeof(IAccelerometerService));

This tells the RootWorkItem that when you call to create an item of type IAccelerometerService, it should actually create an object of type SimulatedAccelerometer.

Each ManagedObjectCollection (so Items, WorkItems, SmartParts and Workspaceas) keeps track of its own Dictionary of type mappings and calling RegisterType on a given WorkItem calls the registration for each.  You can explicitly call RegisterType on any one of the collections directly and "override" an existing registration.  This means that you could have Items create one type and SmartParts create another type for the same interface.  I'm not sure when you'd ever want to do that, but I've provided for it.

Hopefully this makes the IoC framework a little more friendly, especially for testing (which is already painful enough for devices), and helps you, as a developer, develop your solutions faster without having to think about the underlying framework.

7/12/2010 6:37:14 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 

Codeplex is in the process of upgrading their servers to TFS2010.  It isn't very clear, however, how to attach to the upgraded servers from older versions of Studio (like Studio 2008, which is required for all device development).  The answer is that you have to install a "forward compatibility update".

7/12/2010 12:58:02 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  |