Showing posts with label Image IO. Show all posts
Showing posts with label Image IO. Show all posts

Saturday, July 26, 2008

Reading Bitmap Objects III

[build 0034] It looks like I have come to the end of DIBs and CGImages - well that is as far as the file format and file generation goes. The final part was, much as I suspected, mainly leg-work.

Below is one of the final parts of my testing - showing transparency working correctly in the output file. The GIF transparency has the characteristic halo as it only supports 0% or 100% transparency.Testing the file generation had a few wrinkles - the file format caters for 8-Bit indexed bitmaps, but I was only able to get the Image IO file import to yield 16bit indexed bitmaps. So in order to test this properly I added code that would convert the 16 bit indexed to 8-bit indexed. In the end the extra leg-work was well worth the effort - the unit tests that I added to check the conversion threw into open some underlying issues that I had in some of the other conversion routines.

Prior to moving on to the next part, I shall probably start looking at text and glyphs, I added a small number of sample files to the Unit Tests of the project - to assert that a file, with images, when written is the same as when it was first read (I create these test files in the PC version of Cello) - the idea is to get an early call on any regression in this area.

Sunday, July 20, 2008

Saving JPEGs with Image IO

[build 0031] JPEGs now work again.

Fixing the problems in the JPEG images fell out in the wash. It looked like a byte ordering problem combined with a pixel size problem (which would explain the vertical striping) - but I did not look into it. Instead I used Image IO to generate the JPEG. The save, using Image IO, fell out in four or five lines of code. Using Image IO means that the OS will handle all the byte order, pixel order, packing etc - all I need to do is to feed it a CGImageRef. As all bitmaps in the OSX version of Cello are now CGImages this is natural. Also as a tiny fringe benefit using Image IO also means that I have been able to remove the JPEG library from the project build, so the file count and build time are modestly improved.

All that remains, to put the issue of bitmaps to rest, is to work through the remaining problems with the other bitmap formats that are not encoded as JPEG. This would seem to be more of a handle turning exercise - there is code missing for the 16-bit indexed bitmap and there may be byte/pixel ordering issues for the 32 bit. Byte/pixel ordering issues happen because the original code expects certain byte orders and I need to move this over to using the more general pixel unpacking code that now exists in the project.

Sunday, July 13, 2008

More Bitmaps - DIBS and CGImage

[build 0029] I have been working on bitmaps and bit images and the whole thing is slowly coming more into shape. The issue that I have been having with bitmaps is that fundamentally the bitmaps that are imported into Cello using Image IO as a CGImage are richer and more complex than those represented by DIBS. A CGImage has notions of byte order (big and little endian) and different data packing. Below is a classic example of the sort of an endian problems that you can get.Additionaly there are wrinkles such as GIF images and PNGs with a color table are imported as indexed 16 bit colors. One byte is an index into the color table the other is transparency. Also internally Cello wants to deal with non premultiplied alpha, where as the export needs the alpha pre multiplied.

In with all of this I have discovered that it is possible to get a direct copy of the pixel data of a CGImage - using CGImageGetDataProvider.

With all of this I have changed my approach a little. I have added a CGBitmapInfo to the OSX version of the MFC bitmap header so that I have an idea of byte order and packing. I have also written, and tested, some templated pixel accessors so I can easily access the various channel packing orders.

All of this needs to be integrated with the existing image export so there is still a fair way to go before the image side of things is wound up.

Thursday, June 26, 2008

CGImage & ImageIO - converting premultiplied alpha

The final part of the DIB (bitmap) equation is reading images. Cello has classes for reading and writing PNG, JPEG and a few formats including PaintShop Pro that are prevalent on windows. The change that I have made here is to use Image IO. Image IO will let you read all the most common formats - and also has support for RAW images (as spat by many digital cameras) amongst other things.

ImageIO is really easy to use you just need to give it something - a URL or a Data provider or whatever and it will turn it into a CGImage. The only issue with CGImage is that it is not possible to get to at the pixel data. In Cello there is a fair amount of code that manipulates pixels directly - so I have to facilitate this.

A solution is to draw the image onto a bitmap where you own the pixels - CGBitmapContextCreate will create a bitmap context and the following code fragment will draw it.

CGRect drawRect = ::CGRectMake(0, 0, width, height);
::CGContextTranslateCTM(context.get(), 0.0, float(height));
::CGContextScaleCTM(context.get(), 1.0, -1.0 );
::CGContextDrawImage(context.get(), drawRect, theImage);
In this there are a couple of wrinkles.
  • CGBitmapContextCreate won't work on an indexed bitmap. So all the images will be converted to either RGB32 or RGBA32. I don't see this as a big deal.
  • CGBitmapContextCreate will only work with premultiplied alpha. Cello assumes the alpha is not premultiplied. The easiest thing to do is to wholesale convert the bitmap to nom-premultiplied alpha after the fact.
The code for this is reasonably straight-forward:

inline UInt32 Unpremultiply(UInt32 c, Float32 inverseAlpha)
{
UInt32 result = UInt32(Float32(c) * inverseAlpha);
result = std::min(result, 255);
return result;
}

void BitmapClass::UnpremultiplyRGBA()
{
UInt8* pRow = reinterpret_cast(m_pBits);
for (int row = m_height; row > 0; --row)
{
UInt32* pPixel = reinterpret_cast(pRow);
for (int column = m_width; column > 0; --column)
{
UInt32 alpha = GetAValue(*pPixel);
if (alpha)
{
Float32 inverseAlpha = 255.0/alpha;

UInt32 red = Unpremultiply(GetRValue(*pPixel), inverseAlpha);
UInt32 green = Unpremultiply(GetGValue(*pPixel), inverseAlpha);
UInt32 blue = Unpremultiply(GetBValue(*pPixel), inverseAlpha);

*pPixel = RGBA(red, green, blue, alpha);
}
else
*pPixel = 0;
++pPixel;
}
pRow += m_iByteWidth;
}
}
It is, of course, entirely non-optimized - but as this will happen as a single hit when an image is read I don't see it as being any kind of bottle neck. If it turns out to soak up time and that this is a problem I will attack it later.