Key Value Observing Improvements

Key value observing is quite a useful tool, no doubt about it. But it has a singularly annoying manner of informing the observer of a change. The -[NSObject observeValueForKeyPath:ofObject:change:context:] method is sent to the observer when a change occurs. It’s up to the implementor to parse the `change’ dictionary to figure out what changed.

When I use observers, I usually want a method called on my class when a change occurs, similar to target/action with UI elements. So my -[NSObject observeValueForKeyPath:ofObject:change:context:] is basically a set of if’s for each key I’m observing that calls the appropriate method.

This turns out to be pretty nasty, especially when you have multiple classes in a hierarchy implementing the method. It wasn’t obvious to me that I needed to call super in this method. Thirty minutes of debugging later, and I was convinced there had to be a better way of doing it.

To this effort, I present two new APIs. To NSObject, I add -[NSObject addObserver:forKeyPath:options:selector:] and to NSArray, -[NSArray addObserver:toObjectsAtIndexes:forKeyPath:options:selector]. The code is available here, or keep reading for the details.

Update 1.1: Two critical bug fixes. Please update to the new version if you have the old one. Also, anonymous svn is now available if you want to use an external. http://svn.shiftedbits.org/public/KVOAdditions/trunk. Continue reading

Posted in Software | 4 Comments

SSE Optimized Compositing

As part of a graphics API I’ve been working on (for my own use, it’s hardly ready for production,) I decided to try learning SSE optimization by making the compositing routine faster.

I came up with an implementation which, according to my tests, is 3-5X faster than the non-optimized version. The optimized version below composites a single color starting at a destination pixel buffer for a specified run of pixels. It uses source over compositing on a RGBA, 8bpc, integer pixel buffer.

The optimized version is presented below:

Continue reading

Posted in Software | Leave a comment

Transparency Layer Slowdowns

As the documentation mentions, a transparency layer allows subsequent drawing to be rendered in a separate, fully transparent buffer before being composited to the destination context. In the absence of a clipping region, this buffer is the same size as the destination context, requiring a context-sized buffer regardless of the actual drawing bounds. Creating a transparency layer for a small section of content, then drawing this layer in a window, for example, results in a window-sized buffer for the layer. When the layer is composited into the destination, the entire buffer must be composited — CoreGraphics doesn’t keep track of where the actual drawing is.

This, of course, is slow, especially for several transparency layers.

To help speed things up, clip the context to the bounds of any drawing in a transparency layer before beginning the transparency layer, such as below:

CGRect artRect = CGRectMake(50,50,100,50);
CGContextClipToRect(context, artRect);

CGContextBeginTransparencyLayer(context, NULL);

/* Draw layer content here */

CGContextEndTransparencyLayer(context);

I’ve created a demonstration program to illustrate the effects of clipped vs. unclipped transparency layers. To see the effects, open Quartz Debug (/Developer/Applications/Graphics Tools/Quartz Debug.app) and turn on “Flash screen updates.” Then, open the Transparency Layer program. There are two windows, the left showing the drawing without clipping and the right clipping to the approximate art bounds. The views are redrawing four times per second. Notice that the unclipped view redraws its entire content while the clipped view redraws only the actual art.

Finally, using -[NSView setNeedsDisplayInRect:] clips the drawing to the specified rect.

Posted in Software | 1 Comment

Leopard CGWindowList* APIs

Leopard brings new APIs at the CoreGraphics layer to gather information about windows on the system. These functions let you find windows relative to a known window, find all windows on the system, including those offscreen, and obtain images of one or more windows as composited by the Window Server.

However, these API’s have an interesting peculiarity: when the documentation says “a CFArray of CGWindowID values” they mean a CFArray of uint32s, not CFNumbers as programmers familiar with CoreFoundation might expect. This holds true for the CGWindowListCreate(), CGWindowListCreateDescriptionFromArray(), and CGWindowListCreateImageFromArray functions, but the two functions that return window information (CGWindowListCopyWindowInfo() and CGWindowListCreateDescriptionFromArray()) wrap all numeric values in CFNumbers.

To create such an array, you’ll have to create it with CFArrayCreateMutable(), such as the following:

/* Make an array with no callbacks */
CFMutableArrayRef windows = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);

/* myWindow is a NSWindow */
CFArrayAppendValue(windows, (void *)[myWindow windowNumber]);

To get the CGWindowID of a NSWindow, you’ll want to use -[NSWindow windowNumber]. Even though the documentation specifies that this isn’t the same number as assigned by the WindowServer, it turns out that it works fine with the CGWindow* APIs.

Posted in Software | 1 Comment

Time Machine Menu

The Time Machine application, when in the dock, enables access to functions for showing Time Machine, backing up now, stopping an existing backup, browsing other Time Machine disks and showing Time Machine preferences. If you’re like me, you’d like access to these features without the app in the dock; unfortunately, you can’t — by default.

I’ve done a bit of digging and was able to implement all but one of these features in a menu extra. Download it and the source here.

And yes, the menu icon doesn’t scale nicely :)

Posted in Software | 4 Comments

Time Machine Exclusions

So Time Machine is a pretty convenient way to backup your machine, and I use it to backup my laptop to an external FireWire drive. Although Time Machine backs up the “whole system,” I assumed there had to be some exclusions, such as cache files or /dev, for example. After a short bit of digging, I discovered that these paths are specified in a standard property list at /System/Library/CoreServices/backupd.bundle/Contents/Resources/StdExclusions.plist

The full list is some 57 items, and is available below. Besides the expected cache items, the list includes some items I thought interesting:

/home
*/Library/Logs
/Users/Guest
/Library/Safari/Icons.db
/.Spotlight-V100

Of these, the only one I find odd is the exclusion of logs. If your system goes haywire such that you restore it completely from a backup, it might be nice to see what went wrong.

[Update] Fixed the path to the StdExclusions.plist file.

Continue reading

Posted in Software | 39 Comments

Quicksilver Plugins

As the Quicksilver site seems to be down right as everyone is reinstalling for Leopard, I’ve packaged the plugins I have into a zip available here. Quicksilver itself is available here. To install, just put the PlugIns folder in your Library/Application Support/Quicksilver folder or double click the plugins.

[Update] Zon Wakest was nice enough to upload additional plugins. Thanks Zon! These are now added into my archive and the combined list is below. If you got the old archive, download just the additional plugins.

[Update2] Quicksilver now links to 3814 instead of 3813.

The plugins included are as follows:

Continue reading

Posted in General | 54 Comments

Pointless Optimization

The other day I needed to write a function to give me a formatted string representation of a quantity of bytes. Pretty trivial function, but nontheless, I found myself optimizing it. I ended up with the following:

static uint64_t count = 7;
static NSString *suffixes[7] = 
    { @"B", @"KiB", @"MiB", @"GiB", @"TiB", @"PiB", @"EiB"};
uint64_t i, c;
for (i = 1024, c = 0; i < (count << 60); i <<= 10, c++) {
    if (bytes < i) 
        return [NSString stringWithFormat:  @"%0.2f%@", 
                                            (double)bytes / (double)(i >> 10), 
                                            suffixes[c]];
}
return @"Big";

Now, optimizing away divisions (except for the final conversion to string) really doesn’t save any time at all. The creation and subsequent autorelease of a NSString will take far longer than the division operations, even for large byte counts. Thus, I believe this code sample qualifies as pointless optimization, although it was entertaining to write.

Posted in Software | Leave a comment

Color Setting Performance

If you’re looking to eek some extra performance out of your drawing code, take a look at how you’re setting your fill and stroke colors. Setting the color using CGContextSetRGBFillColor() or creating a new CGColorRef/NSColor object each time you set the color is quite expensive to do each time you draw. Depending on how many colors you use in your drawing, consider caching the CGColorRef objects and using CGContextSetFillColorWithColor() or -[NSColor setFill] instead.

A CGColorRef is 48 bytes for a RGB color. A NSColor is 32 bytes for a RGB color, plus 48 bytes for a CGColorRef. If you can afford the memory hit, you’ll see quite a bit of speed improvement from caching colors.

Below is the output from a small test app (source available here) that tests various color setting methods: (Each test is 1,000,000 iterations.)

CGContextSetRGBFillColor: 4.368422 seconds
CGContextSetFillColorWithColor, static CGColorRef: 0.599552 seconds
-[NSColor setFillColor], static NSColor: 0.832328 seconds
-[NSColor setFillColor], dynamic NSColor: 5.847799 seconds

Posted in Software | Leave a comment