Welcome to Atalasoft Community Sign in | Help

My Favorite DotImage 8.0 Feature

We just released DotImage 8.0 and I’m very happy with the overall process.  In previous releases, I created some enabling technologies.  These are features or structural changes that might not offer an immediate return on investment, but will over time.

For example, in DotImage 5.0, I introduced the notion of PixelMemory and PixelAccessors as a way to abstract out where image memory comes from and how to get at it without necessarily having to resort to unmanaged code.  In this process, I obsoleted the AtalaImage property ImageData so that it would warn you about using it and created adapter code to reimplement it in terms of PixelMemory.

In DotImage 8.0, I was able to usher in two new enabling technologies.  The first is that PixelMemory can now be “movable”.  By movable, I mean that PixelMemory may be in one of two states, locked and unlocked.  When PixelMemory is unlocked it is free to move in memory.  This means that PixelMemory can potentially be unloaded out of main memory if need be.  As an example, we now allow PixelMemory to come from the managed heap by using the ManagedPixelMemory class.

The second technology that I added is  the PixelMemoryFactory.  PixelMemoryFactory is a singleton class that is used for allocating PixelMemory objects.  All of DotImage uses this mechanism for creating PixelMemory.  All our codecs, all our image processing routines and so on now go through the PixelMemoryFactory.  Further, while we provide a reasonable default for memory allocation, you are free to change it to what you think might be best.

This change in behavior comes with a few minor costs.  If you have been writing your own direct image access routines that use PixelMemory and PixelAccessor objects and only create images from AtalaImage constructors, then you don’t need to change a thing.  You’re awesome – you paid attention to the warning from the Obsolete attribute attached to AtalaImage.ImageData!  If, however, you used ImageData, you will need to change your code.  PixelAccessors really are the way to go.  If you are intent on working with pointers directly, you must call PixelMemory.Lock before you work with a pointer from image memory and you should call PixelMemory.Unlock when you’re done.  If you implemented an ImageCommand in terms of our boilerplate, your override of PerformActualCommand will always be passed images with memory that is locked.

The good news is that if you’re working in C#or VB, we give you a tool to make those rules trivial to enforce: PixelMemoryLocker.   PixelMemoryLocker is a class that implements IDisposable such that upon construction it will lock pixel memory and on dispose it will unlock it.  This means that you can write code like this:

private void ProcessImageDirectly(AtalaImage image)
{
    PixelMemory.ThrowOnNonContiguous(image);
    using (PixelMemoryLocker locker = new PixelMemoryLocker(image.PixelMemory)) {
        IntPtr p = PixelMemory.PixelDataFromPixelMemory(image);
        // typical routine
        ProcessData(p, image.Height, image.Width, image.RowStride);
    }
}

PixelMemoryLocker will also operate on an AtalaImage in addition to a PixelMemory object, so I could have written:

using (PixelMemoryLocker locker = new PixelMemoryLocker(image)) { /* ... */ }

if I wanted to be more terse.

As an added bonus, the tool I used to create PixelMemoryLocker is also available to you.  ResourceReleaser<T> lets you define classes that need to perform a specific action on construction and another on disposal.  I blogged about it earlier here.

There are a couple caveats today:

  1. Don’t ever assume that memory is locked unless you have locked it or unless Atalasoft has specified it.  Locking, in most cases, is cheap or free.  Multiply locking memory incurs close to zero cost.
  2. Don’t every use PixelMemoryLocker outside of a using block.  I could add, “…without ensuring that Unlock will be called in a predictable manner.” but I simply can’t stress enough that letting the GC call Dispose will create some pretty terrible, hard to reproduce bugs.  Just put it in a using block.

There are also a couple caveats for the future:

  1. Don’t ever assume that image memory is contiguous.  DotImage is already written to protect itself from that possibility.  Every routine that requires that memory is contiguous will first call PixelMemory.ThrowOnNonContiguous().  You should too.
  2. Don’t ever assume that an image is always in memory.  The current infrastructure supports the virtualization of images so images can be free to come and go.

I said earlier that these were my favorites.  I have three main reasons for this – one is that in making this change, I was able to completely change how memory was allocated and after the initial period of work, there were two unit test failures, each of which took a few minutes to repair.  I’m very proud of that.  The second is that this enabling technology leads DotImage in a direction that will nearly transparently lead to some very powerful image management with very little (if any) work on the client side.  Finally, this change creates some low-level flexibility which is easy to use and easy to manage.  We like flexibility because it enables you to do more than you imagined.

Published Thursday, September 03, 2009 9:35 AM by Steve Hawley

Comments

No Comments
Anonymous comments are disabled