Thursday, October 30, 2008

Understanding Fixes - Debugging

As I work trough the text effects I make test files on the PC version so I can test on the OSX version. The test files are reasonably straight forward - I test the range of settings, use different styling and a mixture of paragraph and character styling. In this I came across a problem. The issue was a word based effect with a change of style within the word resulted in some characters being badly positioned. The screen shot below is an example is the output from the simplest case I could create that demonstrated the problem - the "r" is kicked down below the base-line.
This bug had me floored - it was just very hard to work out why it was not working. Running the PC and OSX versions back to back can be very helpful but the in the case of text and fonts there are a lot of numbers many of which are different on the two platforms. CoreText , for example, gives different ascent and descents for fonts. Much of the math in Cello is integer and CoreText is fully floating. As an example of some or the sorts of differences you can get [even] Arial is different on the PC than then Mac. As an example below is a comparison between the same glyph in Arial on the two different platforms.

Getting nowhere for a while I wrote a list of all the possibilities that might bet the cause problem and worked through them. This got me as far as the point where the offset for positioning a glyph within an effect block was calculated. I discovered that if I abandoned the offset calculation, for the glyph that was failing, and set the offset to 0 (so it was the same offset the other glyph) everything worked!

OK- so this was a natural time to give up the chase and move on - but it really niggled me. I had no idea why forcing the offset to zero got things to work. I could kind of imagine that the offset might be zero in the PC version, or that CoreText was giving me values that were different enough for somehow this to be right or something. Checking on the PC version there was an offset that was different from mine but definitely not zero and not that far away.

Moving on and saying "well sure it works now" was tempting. I have worked for many years with a really excellent programmer who is careful, methodical, and very good. When things happen and start mysteriously to work wants to know why. He also, incidentally, writes some of the best code I have seen. I have learnt a lot from him - I carried on the chase. This morning before work I found the problem - misunderstanding some of the code I was not aggregating separate styles within a common word into the same effect block. This was an important bug to find and my setting the offset magically to zero would have been a bodge that would just have masked the problem.

Saturday, October 25, 2008

Text Effects - Debugging rounding errors

The first of the text effects is an effect where glyphs are swapped our randomly. Having fixed the more obvious (large scale) problem in the positioning (as described here) there was an outstanding more subtle bug in the positioning of the effect glyphs.

One of my test files is based on some effect text that is precisely positioned on top of some normal text. When the effect ends the text come to rest so it exactly covers the normal text. In the screen shot below you can see that there is a vertical and horizontal offse that looks a bit like a drop-shadow.These offset problems were, as I suspected, due to rounding errors. The PC version of Cello deals exclusively with integer values when positioning text. CoreText, on the other hand, deals with floating point values. The output format deals with integer TWIPS. The issue with the effects is that Cello carries about bound rectangles for the effects (in this case glyph-by-glyph) that are stored as integers before being multiplied by 20 to become TWIPS.

Once I uncovered the problem (it took a while to work out where it was going wrong) I replaced the CRect (a MFC integer rectangle) with a floating point equivalent. Ultimately I need to go through all of the text calculations and make sure that floating point is used - but this is something for my to-do list rather than for now. At the moment I want things to work - but also the minimum of disruption in getting the effects to build and work.

Thursday, October 23, 2008

Text Effects - Debugging bounds problems

I have been looking at a strange problem where glyphs, when revealed by an animation, fail to redraw properly. What happens is when a part of an animation moves over a character shape the shape that is revealed sometimes fails to redraw. You can see this in the snapshot below - most obviously there is a bit missing from the A.


When I first saw the problem I presumed that it was down to a bug in the player and dismissed it as "just something that happened" however as time has gone on I have noticed other situations where the same problem. Going back to the PC version of Cello I can see that the problem does not exist - so obviously it is a problem that I have introduced.

I must admit to being daunted when I first looked at the problem - I had little idea why the problem would happen, but like most bugs in the end it was not as bad as I thought. The key things are to take the time, think it through, and get a plan that will at the very least narrow the problem down.

The example animations I was working with were, in terms of the output, quite complex. The examples had a fair handful of glyph shapes. The first thing I did was to create a sample that was as simple as possible. I found I could get the problem to occur with just two characters that were on two different lines. I could also further simplify the example by using an effect on simple shape that was on top of the characters. If I made the characters the same this was a further simplification. These simplifications were important - the type of the shape moving seemed to me unimportant - the problem seemed to be in the glyphs that were being revealed.

The next part of the debugging was to compare the output against that of the PC version. The Engineer who originally wrote Cello did a really good job. Amongst the things he did was to add in debugging code that would parse the resulting file and output it as text. What this means is that I can run the PC version of Cello, then the OSX version and diff the two files. I have done this in the past when working on glyphs and text and it is invaluable.

Looking at the diff of the file I found a few things. The PC version of Cello had an extra redundant curve segment - effectively a moveto(0,0). I changed the code so it was added. The OSX version output an empty glyph (for the so I changed the code to stop this from happening). Working through the diff the next thing was the defineTextTag - the bounds were different - and looked wrong. And this turned out to be wrong. My basic error was in constructing a CGRect I hat the origin of the Rect at the top rather than the bottom. The player will happily draw outside the bounds - but it respects the bounds when calculating a redraw area. Seems reasonable enough to me.

Tuesday, October 21, 2008

Text Effects - Debugging

Having implemented the Accessor class getting the base class and the first text effect to compile was reasonably easy. The first text effect is a glyph substitution effect where glyphs in text are replaced with random glyphs in a simple animation.

After fixing a couple of crashes the output appeared to more or less work. To test the effect more thoroughly I overlaid the text with the effect applied (a single word) on top of the same text without the effect — this allows me to check things like positioning and character spacing. Doing this and publishing revealed that the text was positioned wrongly and had a vertical offset — so there is a bug.

After quite a few years of chasing bugs a method that often really helps me is to stop and think about it and see if you can figure out how it is misbehaving. Are there different — maybe better test cases that will tell you more about the problem.

So on to this offset — is it a one off offset (say the position of the text box) that is causing the problem or is it something that is incorrect with the calculation of the baseline. Knowing this will allow me to more tightly narrow my focus. There are a lot of numbers flying about in Cello and being able to look in the right area is going to be much less painful.

To test this I made two lines of test (two paragraphs with one word) in the PC version of Cello and published. If you look at the results below:



So from this it looks like the offset increases with successive lines. My guess is that the problem is somewhere in the calculation of the baseline or bounding rectangle of the text — both of which have logic to flip from CG style coordinates to QD style coordinates.

Thursday, October 16, 2008

Less is More

The observation Less is More is an observation of Cello project that comes home time and time again. A considered path of least resistance seems generally to be the best course.

For the past week or so I have been working on the text effects and I had sort of stalled. Cello is an after hours project so it is hard to get a stretch of more than a few hours together. The text effects stalled because of the complexity of reworking the effects base class so that it would deal withe a CoreText view or the world (CTLine, CTRun, CFGlyph, CFString) rather than the inbuilt text. Ultimately if it takes a cold towel on your head to figure out what to do something somewhere is probably wrong.

Access to the old pre-CoreText text was made through an Accessor class that iterated through lines and style runs. My mistake was to not use or implement it. I went straight to CoreText for the text output - it seemed right I needed to add the distinction between glyphs and characters. The crunch was that the Accessor class is a component that is passed about for the text effects.

The better solution is instead to use and re-implement Accessor class so it sits on top of CoreText and change it so that it can handle glyphs. This is not hard to do and essentially preserves the way that text effects are written.

My approach here is to write the new Accessor class and go back to my text export (which works) and deploy it. I have good test files and an understanding of how it should work. I can also shape the Accessor class in place and round it in place if necessary. Then I will go back to the text effects base-class and plumb in something that may have a different thread but is of the same bore.

Sunday, October 5, 2008

Managing TextBreakLocatorRef using Boost

Since the birth of OSX new data types are based on Core Foundation. Core Foundation provides an owner-counted management of objects which allows for quite a natural, as well as simple, memory management. In C++ these are easily managed by boost intrusive_ptr (more details of which were posted here)

My initial take on TextBreakLocatorRef was that it predated Core Foundation and as I wanted to be able to manage these pointers using smart pointers I looked into how I might do this using boost and shared_ptr.

shared_ptr provides an owner counted reference to an object. The smart pointer is more complex than a simple pointer and manages an owner count. It is an very easy and reliable way to provide smart pointers to objects. The templated constructor of the shared_pointer permits the specification of a custom dispose (a custom deallocator). A reference to this deallocator is maintained by the smart pointer it's self.

So using shared_ptr you could manage TextBreakLocatorRef like this:
The only funny being that you would need to define opaquetextbreaklocatorref so that boost will consume it.

However investigating the issue further it seems that TextBreakLocator is managed by core foundation under 10.5. NSLog lists the locator as follows:
Consistency is always a good thing so I will use boost intrusive_ptr as I have done elsewere in Cello.

Unicode Word Breaks - UCFindTextBreak

Working through the various effects I have come quite quickly to text - so the holiday (the appearance of rapid progress) is over and it is back to text. I like text so all is right with the world.

The text effects work on either lines of text, words within lines or letters. In the PC version of Cello there is no distinction between a letter and a glyph - however gunning everything through CoreText things are a bit different and there is this distinction to deal with.

The issue of word breaks is also more complex if you are dealing with Unicode. The PC version of cello deals only with Roman text and the word breaks are based on white-space. This does not hold for all languages and character sets - for example Japanese words are not bounded by white-space so this method of detecting word breaks (obviously) won't work in the general case.

A solution to the detection of word breaks is to use Apple's Unicode Utilities which has a Text Break Locator that discover text breaks. The format is reasonably simple UCCreateTextBreakLocator creates a text break locator, UCFindTextBreak will then locate a text break in a run or UTF16 characters.

The amount of wriggeling to get UCFindTextBreak to work with a CFString is very modest.

Wednesday, October 1, 2008

Core Text - CTFontGetUnderlineThickness

[build 0066] I have got the first five effects to work - that is I can open files that have the effect and publish them. My thought of a chance to make some rapid progress by avoiding text was short-lived. Having added text to the test files there were problems to work through.

The effects deal with text as path shapes. That is internally they make a single shape out of the glyphs and then do weird and wonderful things to them. This exercises a section of the text code that I had identified as not being tested and was open on my check-list (I could not generate file that was able to test it). The primary problem was due to incorrectly scaling the glyph shapes as they were generated, but there were a few others.

Working through the bugs the last one was the position of the underline. Below is an effect that transitions a shape my curling it upwards. In the test file je orange text starts exactly over the bluish text and curls off. It is caught part way through transition.

Interestingly a similar problem affected the PC version of Cello - the underline shifted once an effect was applied. The two underlines are calculated in two different place and calculated differently.

The solution was quite nice. CoreText has two functions CTFontGetUnderlineThickness and CTFontGetUnderlinePosition - just call these functions in the two places and the results are identical.