Archive for October 2006


Hazel 1.1

October 26th, 2006 — 8:03pm

It’s here. A bunch of improvements and fixes, including the ability for rules to execute Automator workflows, AppleScript and shell scripts. Since this blog is a bit more developer oriented, maybe one of you out there can come up with some interesting scripts.

This release also incorporates Sparkle for software updates. In actuality, I’m using the Sparkle+ variant, though I’m not using the system profiling aspect of it. If any of you are developing a pref pane and want to incorporate Sparkle, feel free to drop me a line as you have to change a few things. Tom Harrington, who is the maintainer of Sparkle+ also has experience doing this but I’m not going to volunteer him for questions though if you join the Sparkle+ mailing list I’m sure he’ll answer them.

In short: Sparkle good. In retrospect, I should have included it from the beginning since you get problems when installing a pref pane over itself. I know that Objective-C can’t unload classes but you’d think that System Preferences would restart when this happens. It does detect it since it asks the user if they want to install over the old one.

At the risk of raising the ire of those Geico cavemen: Download. Buy. *grunt*

Comment » | Hazel, Noodlesoft, Software

Previewing NSColors

October 16th, 2006 — 12:40pm

Many of you probably know about this already but for those who don’t, here’s a nifty little tidbit for you developers.

NSColor has all those methods to retrieve system colors. Wouldn’t it be nice to see what they actually are? Bring up a color panel (doesn’t matter which app), select the color list icon (it should be the 3rd icon) up top. In the “List” pop-up, select “Developer”.

color-panel.png

What you get is a list of colors with NSColor’s method names. In addition, it seems to update if you change your color scheme in System Preferences.

Not that this clears up all questions. Still not sure where those purple “knob” colors appear.

4 comments » | Cocoa, OS X, Programming

More Fun With Gradients

October 11th, 2006 — 4:11pm

For the past couple weeks on and off (mostly off), I’ve been working on this gradient code. In my previous post, I mentioned my attempts at vectorizing it which resulted in no performance gain in the end. Here, I recount a tale of writing a generic gradient method in a NSBezierPath category. Yes, a tedious story, but this is a blog after all, plus you stand to get some actually-useful, free source code in the end so bear with me. Or, like a kid pouring all the cereal out of the box to get the glow-in-the-dark prize inside, you could, through the power of the scrollbar, skip all this and just grab the code.

So yes, I wanted to write a category to provide a method to fill the path with a gradient. Of course, you need to provide a start and end color but I also thought, why not also provide an angle? I didn’t need to do gradients at arbitrary angles (the only gradients I need right now are vertical) but when writing this stuff, I like to make it generic. When Apple starts using -23.728° angle gradients all over the place, I’ll be ready.

Now, the issue is figuring out the starting and ending points for the gradient given an angle. You want the gradient to fully fill the path. Let’s take filling a rectangle at a specific angle:

gradients-figure1.png

In the figure, with an angle α and a starting point A, you’d want the gradient to end at B to maintain the angle and also fill in the opposite corner of the rectangle. Recalling the trig and geometry from the deep recesses of my mind, I came up with the following equation (using known quantities w, h and α):

x = sqrt(h2 + w2) * cos(α - atan(h/w)) * cos(α)

I was a little disappointed that this didn’t reduce down to something a bit more tidy (makes you appreciate E = mc2) but it did seem to work. The problem: not every path is a rectangle. While using the above does cover whatever path may be bound by that box, it is not optimal. Another diagram is in order.

gradients-figure2.pngsadsun.png

In this example, there’s slop at the start and end. Imagine pulling a squeegee from point A to point B. There’s a gap before you hit the shape starting from A and then a gap after the shape before you hit B. How does this affect the gradient? Why is Mr. Sun sad? See below:

gradients-figure3.png

All these diagrams were done in OmniGraffle (except for Mr. Sun which obviously required more sophisticated software). You can see that the “L” shape on the left has a truncated gradient (the black is somewhere where the upper right corner should be). As a user, I’d expect that if I had specified a gradient from white to black, that I would get that range within the shape, otherwise, I would have specified gray as the darker color. It appears OmniGraffle is shading according to the bounding box. Here’s another picture:

gradients-figure4.png

The first shape I freehanded with the correct orientation. The second is the “L” shape from the previous diagram rotated. Notice the difference in gradients. It seems to me the user should not need to care how the shape was originally created. The gradient should be white to black in both cases. By the way, I’m not picking on OmniGraffle; I was using it for these diagrams and decided to test it’s behavior in this regard since I had it running. My guess is that this is the common behavior for software implementing gradients with rotation.

If someone specifies an angle and start and end colors, then the final result should be at that angle and have those start and end colors visible in the gradient and not cut off, dag gummit! I feel in the ideal case, you should really be filling the shape with a gradient, not just providing a window to what ever portion of the gradient happens to be lined up under the shape.

The problem here is that we are using the bounding box in the original coordinate system. What would be ideal is to calculate the bounding box in a rotated coordinate system so we can get the minimum span along the axis of the gradient (the line going through A to B in the first two diagrams).

gradients-figure5.png

In the case above, we want the bounding box on the right.

My initial approach was to apply the path to the current state, transform the coordinate space to the rotated space and get the bounding box there. The problem with this is that I was getting the bounding box of the bounding box in the original coordinate system. In a sense, I was getting what I originally calculated above. The solution was to transform the path and not the coordinate system.

After some futzing and some stupid mistakes which sent me down some dead-ends, I got it working. Rotate the path, calculate the bounding box and set the start and end points to the edge of the bounding box that corresponds to the gradient axis, then do an inverse transform back on those two points to get them back into the original coordinate system.

What does this get me? Well, for me personally, not much as I mentioned before I only needed to do a vertical gradient. But you, gentle reader, get to enjoy the fruits of my labor. This code is provided under MIT license. If you use it then just mention me and the Noodlesoft site wherever you put your attributions. And of course, if you have any bug reports, fixes, suggestions or whatever, send them my way so I can address/incorporate them.

Noodle Gradient Test + NSBezierPath-NoodleGradient category

5 comments » | Cocoa, Downloads, OS X, Programming, Quartz

Productive Waste of Time: Gradients and Altivec

October 5th, 2006 — 12:20pm

I was writing some gradient code and while writing the gradient function it occurred to me that it could be vectorized. I was doing the same operation in a loop to two sets of 4 floats (the red, green, blue and alpha values that need to be calculated for the gradient). The 4 floats fit perfectly into a 128 bit vector. My reason for doing this was not because of any bottleneck observed in Shark; it was just a flimsy excuse to play with Altivec for the first time. Nonetheless, if there was a noticeable performance increase, all the better.

In between a wedding, entertaining friends who were staying with me and getting a beta release of Hazel out the door, I read a little on Altivec and cooked up a little test program. You can download the source below. It’s PowerPC with Altivec only. I was sloppy with the #ifdefs but compiling the test without Altivec support is a bit pointless. I don’t have an Intel machine so I didn’t write an SSE version. For you Intel readers at home, feel free to add the appropriate SSE code (and let me know what you come up with). You’ll find my guess at what the SSE version is supposed to look like commented out in the code (but don’t trust it; I don’t exactly know what I’m doing here).

What does the test show? Well, at least on the G4 and G5 machines I have available, performance is roughly the same.

Of course, there are two ways to interpret this:

My test sucks
(obligatory remarks: “I didn’t know they taught programming at clown college.”, “My [insert feeble relative] can code better than you”)

A very likely possibility. This is my first foray into Altivec and I just started delving into Core Graphics gradient functions last week. As far as my Altivec code goes, I am still quite fuzzy on whether I should have used something like vec_ld or vec_splat to turn the multiplier into a vector though I’m pretty sure that using the float[]/vFloat union fulfills the byte alignment requirements (though it’s possible that it’s slower). I’m guessing it’s the difference between sticking it in a register and having it in memory. If any SIMD experts could educate me on this, I’d appreciate it.

Vectorization is not effective in this scenario

I’m sure those much more experienced with Altivec can explain this without resorting to a tactic that we like to call “making stuff up”. Me, I am going make stuff up. So here goes: I’m only computing one vector at a time and I’m doing all these scalar to vector (and back) conversions. My guess is that the overhead outweighs the reduced instruction count in the computation. If the CoreGraphics functions were structured such that it gave you all the values at once (or at least in chunks) instead of at each step of the gradient, then I could see an argument for vectorization made here.

So for now the result is inconclusive until I can get someone who knows what they’re doing to either verify or refute my test. Try it for yourself and let me know (especially if your results differ from mine). And please point out any deficiencies in my implementation (though comments on coding style can go to /dev/hell).

GradientTest (PowerPC with Altivec only)

2 comments » | Cocoa, Downloads, OS X, Programming, Quartz

Back to top