Archive for February, 2008

What to do if Core Image is ignoring your slider attributes

Wednesday, February 27th, 2008

So you're writing an Image Unit, and it has a couple of numeric parameters. You expect that Core Image Fun House will show a slider for each of them, and it does—but no matter what you do, the slider's minimum and maximum are both 0. Furthermore, Core Image Fun House doesn't show your parameters' real display names; it simply makes them up from the parameters' KVC keys.

The problem is that you are specifying those attributes in the wrong place in your Description.plist. And yes, I know you're specifying them where the project template had them—so was I. The template has it wrong.

One of the filter attributes that Core Image recognizes is CIInputs. The value for this key is an array of dictionaries; each dictionary represents one parameter to the filter. The template has all the parameter attributes in these dictionaries. That makes sense, but it's not where Core Image looks for them.

In reality, Core Image only looks for three keys in those dictionaries:

  • CIAttributeName
  • CIAttributeClass
  • CIAttributeDefault

Anything else, it simply ignores.

The correct place to put all those other keys (including CIAttributeSliderMin, CIAttributeSliderMax, and CIAttributeDisplayName) is in another dictionary—one for each parameter. These dictionaries go inside the CIFilterAttributes dictionary. In other words, the CIFilterAttributes dictionary should contain:

  • CIInputs => Aforementioned array of (now very small) dictionaries
  • inputFoo => Dictionary fully describing the inputFoo parameter, including slider attributes and display name
  • inputBar => Dictionary fully describing the inputBar parameter, including slider attributes and display name
  • inputBaz => Dictionary fully describing the inputBaz parameter, including slider attributes and display name

Finally, an example:

<key>CIFilterAttributes</key>
<dict>
    ⋮
    <key>CIInputs</key>
    <array>
        <dict>
            <key>CIAttributeClass</key>
            <string>CIImage</string>
            <key>CIAttributeName</key>
            <string>inputImage</string>
        </dict>
        <dict>
            <key>CIAttributeClass</key>
            <string>NSNumber</string>
            <key>CIAttributeDefault</key>
            <real>1.0</real>
            <key>CIAttributeName</key>
            <string>inputWhitePoint</string>
        </dict>
        <dict>
            <key>CIAttributeClass</key>
            <string>NSNumber</string>
            <key>CIAttributeDefault</key>
            <real>0.0</real>
            <key>CIAttributeName</key>
            <string>inputBlackPoint</string>
        </dict>
    </array>
    <key>inputWhitePoint</key>
    <dict>
        <key>CIAttributeClass</key>
        <string>NSNumber</string>
        <key>CIAttributeDefault</key>
        <real>1.0</real>
        <key>CIAttributeDisplayName</key>
        <string>White point</string>
        <key>CIAttributeIdentity</key>
        <real>1.0</real>
        <key>CIAttributeMin</key>
        <real>0.0</real>
        <key>CIAttributeMax</key>
        <real>1.0</real>
        <key>CIAttributeName</key>
        <string>inputWhitePoint</string>
        <key>CIAttributeSliderMin</key>
        <real>0.0</real>
        <key>CIAttributeSliderMax</key>
        <real>1.0</real>
        <key>CIAttributeType</key>
        <string>CIAttributeTypeScalar</string>
    </dict>
    <key>inputBlackPoint</key>
    <dict>
        <key>CIAttributeClass</key>
        <string>NSNumber</string>
        <key>CIAttributeDefault</key>
        <real>0.0</real>
        <key>CIAttributeDisplayName</key>
        <string>Black point</string>
        <key>CIAttributeIdentity</key>
        <real>0.0</real>
        <key>CIAttributeMin</key>
        <real>0.0</real>
        <key>CIAttributeMax</key>
        <real>1.0</real>
        <key>CIAttributeName</key>
        <string>inputBlackPoint</string>
        <key>CIAttributeSliderMin</key>
        <real>0.0</real>
        <key>CIAttributeSliderMax</key>
        <real>1.0</real>
        <key>CIAttributeType</key>
        <string>CIAttributeTypeScalar</string>
    </dict>
</dict>

You can see how the descriptions under CIInputs are as terse as possible; everything besides the absolute necessities is specified in the outer dictionaries.

Apple Bug Friday! 74

Friday, February 22nd, 2008

This bug is Typo in syslogd(8) manpage. It was filed on 2008-01-19 at 15:41 PST.

(more...)

Multi-stroke key bindings, Extended vi Edition

Thursday, February 21st, 2008

As some of you know, I use the multi-stroke key bindings by Jacob Rus to easily type strings such as ⌃⇧⌘⌫. I mostly use these characters in three places:

  1. Correspondence with users
  2. Posts here
  3. Documentation

And it sure beats looking those characters up in UnicodeChecker or the Character Palette.

But I was never satisfied with the original set of key bindings that that file provides. The first problem that I had was that the arrow keys are emacs keys, not vi keys:

Solid Left (back)⌃M + ⌃B
Solid Right (forward)⌃M + ⌃F
Solid Up (previous)⌃M + ⌃P
Solid Down (next)⌃M + ⌃N
Dotted Left (back)⌃M + B
Dotted Right (forward)⌃M + F
Dotted Up (previous)⌃M + P
Dotted Down (next)⌃M + N

I'm a vi guy, so I wanted the hjkl arrangement for my arrows. So that was the first change I made.

I also added a bunch of characters, and reassigned some of them:

Additions:
ChDescriptionKey sequence
Return^M + Return
Command^M + C
Command (Apple)^M + ^A
Option^M + ^O
Control^M + ^C
Shift^M + ^S
Caps Lock^M + ^S
Smiling face^M + ^F
Frowning face^M + F
×Multiplication sign^M + ^X
Modifications:
ChDescriptionKey sequenceReason for modification
The arrows (solid and dotted), as noted aboveBecause I prefer vi to emacs
Return^M + ^RTo free up ^E for Escape
Enter^M + RTo free up E for Eject
Home^M + Shift-^HTo free up ^H for ←
End^M + Shift-^ETo free up H for ⇠
Escape^M + ^ETo free up ^X for × (multiplication sign)
Eject^M + ETo keep Eject on the same key as Escape

In case you're wondering about the division sign (÷): The OS already provides this as ⌥/, and always has been (all the way back to 1984). I didn't need to add it.

Also, in case you're wondering about “Apple” above: The Apple logo is ⇧⌥K, so I didn't need to add that, either. (Plus, I never use it.)

File: DefaultKeyBinding.dict.bz2

This file contains all of the above bindings, as well as the ones from Jacob Rus' original that I didn't change.

To install it, put it in the KeyBindings folder in the Library folder in your Home folder. You may need to create the folder. The bindings are loaded on application launch by Cocoa, so you will need to relaunch any already-running applications in order to use the bindings in them.

How to convert an alpha channel to a mask

Monday, February 18th, 2008

Updated 2008-04-17 to clarify the marked-up screenshot of the Color Matrix view. If you've seen this post before, check out the before and after.

So, let's say you want to convert an image to a mask.

Triangle, circle, rectangleMask from triangle, circle, rectangle

This is easy to do with the Color Matrix filter in Core Image.

If you've ever looked at the Color Matrix filter out of curiosity, you were probably frightened by its imposing array of 20 text fields:

The fields are in five rows of four columns. The first four rows are “Red Vector”, “Green Vector”, “Blue Vector”, and “Alpha Vector”. The fifth row is “Bias Vector”; unlike the others, it is an addend rather than a multiplier, and it does not correspond to a color component.

Don't worry. The fields are actually very simple, though not explained in the UI:

  • The Red, Green, Blue, and Alpha rows each represent a component of an output pixel.
  • Each column represents a component of an input pixel.
  • Each cell in the component rows is a multiplier.
  • Each cell in the “Bias vector” row is an addend.

This image expresses graphically the same explanation that the previous list provided.

(The documentation for the Color Matrix filter actually does explain it, but I like my explanation better for not using fancy math terms like “dot product”.)

So with this tool, our task is redefined like so:

How to replace the color channels of an image with the alpha channel, and set the alpha channel to all-100%

  1. Set the three color-component vectors (Red Vector, Green Vector, and Blue Vector) to 0, 0, 0, 1. (In other words, multiply every input color component by 0, and the input alpha by 1, and set all three color components to that.)
  2. Set the Alpha Vector to 0, 0, 0, 0. (In other words, multiply all input components by 0, and set the output alpha component to that. In other other words, set every output alpha component to 0.)
  3. Set the Bias Vector also to 0, 0, 0, 1. (In other words, add 0 to all three color components, and add 1 to the alpha component.)

You can generalize this to the extraction of other channels. Let's say you want to make a mask of the blue channel:

  1. Set the three color-component vectors to 0, 0, 1, 0. (For every output color component, multiply every color component by 0, except for blue. Multiply blue by 1—i.e., don't change it.)
  2. Set the Alpha Vector to 0, 0, 0, 0. (Multiply every alpha component by 0—i.e., set every output alpha component to 0.)
  3. Set the Bias Vector to 0, 0, 0, 1. (Add 1 to the alpha component. This step is invariant; you always add to the alpha component.)

To demonstrate this, here's a red-blue gradient (shown in Acorn  to visualize the gradient image's own transparency):

The gradient image is an oval, filled with an upper-left-to-lower-right red-to-blue gradient, on a transparent background.

If we extract the blue channel, as shown above, we get this:

A mask where the blue parts of the source image are white, and all else is black.

Note how the red parts of the gradient are black, because we extracted the blue channel, and there was little to no blue there.

Likewise, if we extract the red channel, we get this:

A mask where the red parts of the source image are white, and all else is black.

In this case, the converse of the blue-channel mask.

(By the way, in case you're wondering: No, I don't know what caused the white pixels along the edge. It could be a Lineform bug, or a Core Image bug, or a graphics-card bug. I didn't keep the original Lineform file for the source image, stupidly, but in case you'd like to test it on your own machine, I re-created it. Here's a PDF of the replica; you can use image to convert it to PNG. I can confirm that I saw similar results with this image to the results with the image I used for this post.)

You can even mix up the colors of an image. Suppose we want to reverse that gradient:

  1. Set the Red Vector to 0, 0, 1, 0. (In other words, replace red with blue.)
  2. Set the Blue Vector to 1, 0, 0, 0. (In other words, replace blue with red.)
  3. Leave the Alpha and Bias Vectors at the default values. (In other words, we're leaving the alpha channel unchanged this time.)

The same oval-shaped gradient image from above, but with red and blue swapped.
The red-to-blue gradient is now a blue-to-red gradient.

So what is this good for?

Well, mainly, so you can create mask images. Several filters require these, such as the Blend with Mask filter in the Stylize category. The Color Matrix filter makes this easy, although you still have to save the mask image somewhere.

It's even easier in Opacity, where you can create a Color Matrix filter layer, configure it using the Layer Inspector, then hide it by clicking its eye icon. This way, the filter layer won't show up in the rendered document (or in any of its build products), but you can still use its result as the mask to another filter layer.

Dear Lazyweb: A Frank Caliendo question

Friday, February 15th, 2008

You may have seen Frank Caliendo's recent Dish Network commercials, in which he does pitch-perfect impressions of several celebrities. Two of the three I've seen are George W. Bush and John Madden.

But, and this is my question, who is this supposed to be?

UPDATE 2008-02-17: I just watched it again, and I think Ian is right—it's Al Pacino. Thanks!

Opacity

Wednesday, February 13th, 2008

As you may have read on wootest's weblog, Like Thought Software released its new image editor, Opacity, today.

Before I go any further, here's full disclosure: The developer invited me to beta-test the app, and I did. He also gave me a free license for this purpose (the app normally costs $89 USD). Also, I have some code in the app, because it uses IconFamily, which I contributed a patch to a long time ago.

OK, that's everything. Now, to borrow from wootest's disclaimer on the same topic:

Don’t confuse this as simple tit-for-tat back-scratching, though. Had I … had no involvement whatsoever, the application would still have been every bit as brilliant, and I would have come out just as strongly in favor of it.

I love this app.

Opacity is an image editor designed to enable app developers to create multiple-resolution and any-resolution graphics easily. It's built for that specific purpose, and the Opacity website even says so. This app really is not intended for anything other than user-interface graphics.

Key points:

  • It's mostly vector-based, but it also has primitive raster tools.
  • It has non-destructive Core Image filter layers, similar to Photoshop's adjustment layers. (Contrast with Acorn, which makes you apply each filter permanently. You can't go back and edit the filter parameters.)
  • It has built-in templates for most common icon types.

Opacity has several important features over past editors:

  • It has built-in support for multiple resolutions. Every Opacity document has one or more resolutions, and you can add and delete them at will.
  • It has a target-based workflow. Each Opacity document is, essentially, a “project” for one image; every target in the document results in one image file in an external format, such as TIFF or IconFamily (.icns). (The application now calls these “factories”, but early betas did, in fact, call them targets, and I prefer that terminology.) You can build each target factory or all targets factories at will, and there's an option to build all whenever you Save.
  • You are not limited to the stock suite of transformations (e.g., Rotate 90°, Scale, Flip Vertical); you can make your own.
  • You can create folder layers to group layers (especially filter layers) together, and these folder layers can be nested as deeply as you want.
  • When configuring a Core Image filter that accepts an image as a parameter (e.g., Shaded Material, Blend with Mask, or one of the Transition or Composite filters), you can use any layer in the document—even folder layers.

Opacity is not perfect. Some things don't quite work like you would expect: for example, vector objects do automatically appear in every resolution, but pixels that you draw or paste don't automatically get mirrored to the other resolutions; instead, Opacity waits for your explicit say-so (the Clone Current Layer's Pixels to Other Resolutions command). Opacity also still has a couple of major bugs: Flip Horizontal, for example, takes way too long in one document that I created. Personally, I didn't expect it to go final this early, and I recommend that you wait until at least 1.0.1.

But those are dark linings in a silver cloud. Once all the major bugs are fixed, I believe that this app is how you will create your application's custom toolbar and button images for the modern resolution-independent world.

Quickie: Finding an svn conflict marker with vim

Tuesday, February 12th, 2008
/[<=>]\{7\}

Weirdest compiler warning I’ve ever seen

Saturday, February 9th, 2008

Here's the code:

[[GrowlApplicationController sharedController] setQuitAfterOpen:YES];

And here's the warning:

Core/Source/GrowlApplicationBridgePathway.m:43: warning: ‘GrowlPreferencesController’ may not respond to ‘-setQuitAfterOpen:’

Wait, what? I said GrowlApplicationController, not GrowlPreferencesController!

The problem is that the compiler is recognizing “sharedController” as a class method, and assuming that I'm using the class that it knows has it—which, in this case, is GrowlPreferencesController. GrowlPreferencesController is declared in its header, which is imported by the prefix header; GrowlApplicationController, meanwhile, is declared only with @class (in another header). So I think it's assuming that GrowlApplicationController is a subclass or something.

However, +[GrowlPreferencesController sharedController] is typed as returning a GrowlPreferencesController *, so when I try to call a GrowlApplicationController method on it, it gives me that warning.

The fix is to #import "GrowlApplicationController.h" so that the compiler knows that GrowlApplicationController has a sharedController method of its own.

Apple Bug Friday! 73

Friday, February 8th, 2008

This bug is ASL_QUERY_OP_REGEX should be a modifier, not an operator. It was filed on 2008-01-18 at 23:27 PST.

(more...)

Generating a tarball of your project

Friday, February 8th, 2008

Every good version-control system has this as a built-in feature.

In the below, $TMP is a staging directory, such as “build”, “/tmp”, or “/Volumes/RAM Disk”. It's also where the final archive will end up.

  • svn: Sort of. svn export $TMP/MyProject && cd $TMP && tar cjf MyProject.tbz MyProject
  • darcs: darcs dist (outputs a .tar.gz file in the current working directory)
  • bzr: bzr export --format=tbz2 $TMP/MyProject.tbz
  • git: git archive --format=tar master | bzip2 > $TMP/MyProject.tbz
  • hg: hg archive -p MyProject -t tbz2 $TMP/MyProject.tbz

List process start dates

Friday, February 8th, 2008

I just wrote a test app that prints the name and start-date of every process that the Process Manager will tell it about. Here it is:

File: ListProcessStartDates-r4.tbz

It's a command-line tool, so you'll need to run it from a terminal. Alternatively, you could wrap the executable in a service using ThisService, then use the service to insert the listing into a text document.

The code to convert the start-date of each process is ugly: Process Manager provides those dates as numbers of ticks since system startup, and I currently go through NSDate to convert those to numbers of seconds since 1970-01-01. I'm fairly sure that this answer is imprecise.

I'd prefer to get the startup time as a number of ticks since some epoch (probably 1904-01-01), so that I can do a more precise conversion. If you know of a way, please post a comment.

Cascading indent filter/service

Thursday, February 7th, 2008

When I respond to a user via email, I sometimes need to refer to a file or folder in the user's file-system. Since users don't ordinarily see path separators, I use a cascading-indent syntax:

    Home
        Library
            Application Support
                Adium
                    Users
                        Default

This works well, but typing all those tabs would be a good way to get RSI in my left hand. So, today, I finally wrote a Perl script to do it for me.

File: cascading-indent.pl.bz2

Of course, I use this script as a service, thanks to ThisService.

One downside to using it as a service is that you can't directly use it in Mail. Mail inserts the contents of the general pasteboard (i.e., it pastes) rather than using the service-communication pasteboard. So I'll need to trampoline through TextEdit for this to work. (This is on my list of bugs to file.)

UPDATE 2008-03-14: Apple fixed this bug in Mac OS X 10.5.2. So now you can invoke it directly from Mail—no trampolining needed.

How to make a 512-px version of the Network icon

Saturday, February 2nd, 2008

You will go from the pure-blue .Mac icon……to the purplish-gray Network icon.

UPDATE 2008-01-02: Ahruman commented that you can just use NSNetwork in IconGrabber. No need to go through all these steps and fake one.

If you've ever needed a high-resolution version of the Network icon for anything, you may have noticed that Mac OS X does not ship with one. When you select the Network and Copy it, then create a new document from the clipboard in Preview or Acorn, the largest size available is 128-px.

Fortunately, the .Mac icon is available in 512-px, and you can easily change it into the Network icon.

You will, of course, need Leopard (for no other version of Mac OS X has 512-px icons).

  1. Obtain the built-in image NSImageNamedDotMac in either Core Image Fun House or Acorn.
  2. Apply a Hue Adjust filter: +5°.
  3. Apply a Color Controls filter: Saturation × 0.25.

The easiest way to get the .Mac image is IconGrabber. Enter the name “NSDotMac”, then click Draw, then set the size to 512×512, then save. (Note: On an Intel Mac, you'll need to build from source, because the pre-built version for PowerPCs doesn't run on Intel for some reason.)

A teleport tip

Friday, February 1st, 2008

If you have multiple Macs, I recommend that you install teleport.

teleport's preference pane lets you lay out your Macs the same way the Displays preference pane lets you lay out multiple displays.

teleport is a software KVM: Simply move your mouse cursor through the edge of your computer's screen into the other computer's screen, just as if it were another monitor connected to your Mac. From then on, your mouse and keyboard control the other Mac, until you move the mouse cursor back through the edge, back to the Mac whence it came.

Here's the tip: When you first set up teleport, it's tempting to turn on the “Switch with a delay” option, on the theory that it will help prevent you from switching accidentally. It doesn't. Turn that off. It's a good theory, but all it does in practice is make it harder to correct an accidental switch, not to mention harder to switch on purpose.

Leave that option off, and it will be as easy to undo an accidental switch as it is to commit one.