Today, for the n-th time (for n > 10) I received a bug report about Iconographer sometimes clipping the right-side third of the 48 x 48 (huge) icon member 1-bit mask. In a fit of constructive procrastination, I decided to look into it. What followed was several hours of delving into code that I had started writing more than 5 years ago. The problem seemed to reduce to something like this: when using
srcCopy into a 1-bit 48 x 48
GWorld with the destination rectangle encompassing all of it, the right-side 16 pixels (of the full height of the image) would get set to white, regardless of what the source was. Changing the mode to
srcXor, shifting the destination rectangle or changing the with of the image all made the problem go away.
Obviously I wouldn't have shipped something like this, so the problem must have materialized in the meantime. Sure enough, this wasn't reproducible on a machine that was still running 10.2. I figured that QuickDraw, especially when operating on 1-bit
GWorlds must not be very high up in Apple's list of priorities, and so this was something that had gotten past their regression tests. The workaround seemed to be to use
srcXor mode, and first clearing the destination area (i.e. setting it to white). This had the same effect as a normal
CopyBits with only a slight performance/complexity hit. It worked as expected, and I figured I could leave it that and begin preparing for a 2.5.1 release in a few days.
However, I wanted to make sure that this was really the case, and perhaps even report the bug to Apple. I made a simple test case app, and discovered that the problem wasn't exhibited there. After much digging around, I traced it to the way I was creating my
GWorlds. Normally this is done with
NewGWorld, however at some point in the distant past I had my my own wrapper for it. The reason was the padding that QuickDraw adds to each row, presumably so that each one can be aligned to a 16-byte boundary in order to speed up memory accesses. However, icon data is stored unpadded in the
'icns' resources and
.icns files, and the back and forth conversion seemed rather tedious to me. I therefore created a wrapper called
NewGWorldUnpadded that would allocate a
GWorld with a slightly smaller width, so that the final allocated size would correspond to the unpadded final desired width. I then had to fix back the GWorld, changing its bounding rect, clipping region, row bytes field, etc. so that it was consistent with my desired size. This required a lot of twiddling with the
PixMap data structures, something I feel isn't very encouraged today. I was in fact very proud of this back in the day, since not only did it make loading/saving easier, but it also cut down on memory use of saved drawing states. I should have realized this tradeoff perhaps wasn't worth it, since two days after releasing Iconographer 1.0 I had to do a 1.0.1 release, fixing a bug caused by this wrapper (since I was using it for selections, which weren't always a multiple of 16 in width, a case I hadn't accounted for).
The problem seemed to be with the manual changing of the
rowBytes field of the
PixMap. Part of this was because there is now a
PixMapExtension struct linked from the
pmExt field, and this struct has a
longRowBytes field that needs to be kept in sync (I believe that 10.2 was more lax about this, since the regular
rowBytes field is enough for smaller values). There are now functions such as
QTSetPixMapHandleRowBytes that would perhaps take care of this, but the solution is very simple. It is now 2004, not 1998, therefore wasting 3K of memory is not a big deal. I can scrap
NewGWorldUnpadded entirely, and just use regular
GWorlds, dealing with the padding as appropriate. This will hopefully fix not only this problem, but also others that I have seen (e.g. rotations sometimes causing crashes). The only downside is that there are 15 places where the wrapper is used, with presumably many more pieces of code that rely on GWorlds having no padding. It will probably take a while to fix.