Wednesday, July 30, 2008

CoreText and Unit Testing

Working through the text the font handling in Cello I find that it meshes well with Core Text. The extent of what I am currently doing requires me to create CTFonts and get glyph curves. This is very similar to some early work I was doing in Cello and as I progress a few modest functions into my Core Text support there is an opportunity to share code.

If you have not used Core Text before it is a little different in terminology - the CTFont is a little more akin to a style than I am accustomed to and carries bit more information than say an ATSUI font (ATSUI will be deprecated in Snow Leopard). For example a CTFont has an idea of size and transformation matrix. Deploying this is not rocket science and it is more a question of jumping through the hoops.

I am unit testing the functions that I write as I add them - also I am taking he opportunity to fill in a few gaps in work that did earlier. For example I have written a simple test for the retrieval of kerning tables. To have any kind of stability unit testing can't depend on fonts on your machine - these can and do vary across system releases. Relying on fonts that are installed on a particular machine is just as fraught.

As a solution I am embedding the few fonts I use in unit test into the test bundle, as I have done with GIFs, PNGs and JPEGs in earlier tests as covered in an earlier post here. At the start of a given test I can activate them using ATSFontActivateFromFileReference, and deactivate them afterwards using ATSFontDeactivate. This means that I can write tests that depend on particular fonts.

Note ATSFontActivateFromFileReference will be deprecated in Snow Leopard - however there is no Core Text function currently available that will activate fonts.

Sunday, July 27, 2008

Starting Text

One of the real pleasures of working on Cello is the quality of the code and commenting. I have started working on text. The two big things left on the file format are text and sounds. Text is, I think, the bigger - but is more familiar to me.The first step is to compile in the registration object and then to work through files that need to be built and included, to add and then build them. The text is a little different from other parts of Cello in that it is complex and has a lot that is dependent on the text engine (D-Type) as well as Windows. Ultimately all of the basics will need to be moved over to Core Text. There is a lot to understand. It is just so nice to open a header file that kicks off like this:
DWATextItemStyle holds style information that applies to a text item. Bit flags for each property determine whether that property is valid in the style. Invalid properties are used for leaving some properties 'as is' when setting the style, and when a property value is not consistent across the whole selection when getting the style.

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.

DIBS and CGImage - The price of progress

[build 0030] Sometimes the price of progress is that things stop working. Not much works in Cello but the JPEG based part of the export now no longer works.

For the past couple of weeks I have been working out the underlying problems with bitmaps, DIBs and CGImages. I abandoned my approach of getting the melding the CGImage implementation to be more like the MFC Dibs and instead more-or-less changed the DIB class so that it was a thin wrapper around a CG image. This was what I originally intended to do but early on the path of lest resistance seemed to be else where.

I don't think getting the JPEG export to work properly will be a big deal and on from that the other graphics formats.

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.

Friday, July 4, 2008

Reading Bitmap Objects II

[build 0027] Looking at the problem with the images it initially seemed like it might be a problem with bounds calculations. Initially I suspected my implementation of CRect intersection operation, that I had added to get the image import code to link, might be at fault. This was a hunch despite the fact that I had written Unit Tests for this essentially trivial function. Like many hunches it was quite wrong.

Later stepping through the code it occurred to me that I could have interchanged a hight and width when reading image dimensions. Again no problem - however I was not far wrong. As I stepped through the code where the DIB is set up from the CGImage I spotted this:
In a move that defies belief (rookie error) all the test images I worked with were 100px x 100px. Corrected results are a little better:

Reading Bitmap Objects I

Cello's file format is broken up into a number of distinct objects. The simplest the shape works and publishes correctly. All the object types are registered through a GUID and the read calls a factory to make objects that correspond to that GUID. This makes for an easy incremental development. All the registration *except* for the shape object is commented out. It is possible to uncomment them bit by bit and to work through them.

The plan is to work through the objects, register them, add the files that are required (working through compile and link errors) and then test.

Having registered the bitmap GUID and worked through the comparatively few errors and additional files required, and a crash in the Color Palette class bitmaps read and write. Publishing yielded a blank (missing) image. This is because the file format, under some circumstances" contains a reference to the image as a hard path. The fall-back behavior at publish time for a missing image is to just omit it.

Having a hard path to an image presents some problems for testing. the work around is to have a mechanism that will change the path in a file after the file has been been read. I did this by having a simple class that has a list of files with full paths and will match a file against the list based on the file name - so
/users/scratch/nonsense.jpg
will match against
C:\Documents and Settings\All Users\Sample Pictures\nonsense.jpg"
This kind of matching is easily done with Core Foundation's CFURL. CFURLCopyLastPathComponent will get the last path component of a path i.e. nonsense.jpg and URLs can be constructed form POSIX, HFS and Windows paths.

Having added the code - it is clear there are some problems with the publishing. See below:

Wednesday, July 2, 2008

Cello - publishing the first file

[build 0025] Cello now publishes the simple test file (just a red square) under OSX.
The publish revealed a single bug - code that was designed to ensure that all the folders in a file path existed, and created them if any were missing, failed. Cello has a class that does this and calls through to one of a small number of utility functions built on top of the cocoa NSFileManager. When writing the utility functions I wrote unit tests but importantly did not for the class that used them. It was left as a note on my todo list - and that was it. Writing the test and making the fix was a ten minute job. Had I done it when I identified the need I would have got a home run (or is it a maiden over) on the first publish, that is it would have worked first time.

Tuesday, July 1, 2008

Cello - opening the first file

[build 0024] Cello will now open a very simple file (just a red square) and save an identical one under OSX.

Getting Cello to open a file brought in a fair number of new files. Objects within the Cello file format are referenced by a unique identifier (actually a GUID) which are constructed from a factory. So adding the code to register the simplest object (a basic shape) with the factory had the effect of bringing in a reasonable number of new files - and once again working through the compile and link errors. The trick here was to be firm with the UI - unlike the underlying files the object files have UI references in them - so I added #if _NO_UI directives about these and their includes to stop any UI being sucked in.

Once I had done this (a couple of days ago) Cello opened her first file without crashing. The question then was "well so the read seemed to work but what did it read?" How can I have any certainty (beyond the fact that it did not crash) that everything was read - the file format is complex. My initial solution was to write the file out again and check that the file could be opened with he PC version of Cello.

Getting the write to work took time - there was a problem in the file class for files with a joint read/write access (there was no unit test for this). Having identified the problem I added the unit test and fixed the code by getting the new unit test to work properly.

When the write worked I then checked the files in HexEdit and they where different - it seems that Cello stores the file path of the file at the point of saving in the file. From here came an idea - adding a way that a chosen file-path can be saved will allow some files to be written that will be 100% the same as the original which in turn will allow unit tests for this to be added. Basically I can create tests that will read and write files (the original being a part of in the test bundle) and assert that the two are equal. I very much want to do this so that I find out as soon as possible if something I do breaks Cello.