Last week, John Gruber and Dan Benjamin released episode 20 of their podcast, The Talk Show. It was devoted to the Apple Extended Keyboard (the Saratoga) and Apple Extended Keyboard II (the Nimitz). This renewed my interest in bringing my own Nimitz back into service using a Griffin iMate.

The Nimitz is the greatest keyboard ever made for the Macintosh. It has the best keys, the best height adjustment, the best Caps Lock key (it physically locks down!)—everything.

One of its distinctive features is a couple of pegs near the top of the keyboard—one near the Escape key, and another near the Power key.

The Saratoga had, printed under the F1 through F4 keys, the words “undo”, “cut”, “copy”, and “paste”. Because these definitions were useless (not to say confusing) to most Mac users, the Nimitz moved these labels to a plastic overlay that came with the keyboard. Those who actually needed it could put it on, which they did by hanging it on those two pegs, and everyone else could simply leave it in the box.

This is a photo by Flickr user penmachine (Derek K. Miller) of an Apple Extended Keyboard II with Apple's overlay, cropped to show the corner of the overlay hanging around the Power key.

That overlay is even more useless today. But I think the idea of an overlay defining the function keys is a good one, especially as Mac OS X has made the function keys actually useful.

So I decided to make a new overlay.

This one does not have the seldom-useful F1–F4 labels. What is <em>does</em> have is labels under F9–F12, listing their default Mac OS X actions (the three kinds of Exposé, plus Eject).

My original plan was to distribute the EPS file for this overlay, and provide instructions on how to customize it.

After writing that it's easy to edit the file, followed by an entire page of instructions how to do that properly, I decided it would be better to write an application to do it for you. I call this application the Apple Extended Keyboard II Overlay Generator.

I've included with the application two ready-made overlays: a replica of the classic Apple overlay; and my Mac OS X overlay. You also have the option of editing them or creating your own from scratch.

Assembly instructions (among other information) and the download are on the webpage.

Blog spam count: 2008-04

 

2008-05-01 19:57:34 UTC

Spam comments blocked by Negative Turing Test in April 2008:

44,573

Blog spam count: 2007-11 through 2008-03

 

2008-04-01 12:10:51 UTC

Average number of spam comments blocked by Negative Turing Test in per month from the start of November 2007 through the end of March 2008:

6,529.8

Total number of spam comments blocked by Negative Turing Test in the same time period:

32,649

Note that this is only on my blog, not any other blogs that use NTT.

Under my MacBook Pro, I use a Targus Laptop Chill Mat. The Chill Mat is an active-cooling pad: it contains two fans that suck in air at the top (i.e., between the laptop and the mat) and thrust it out the back.

And it is a piece of crap.

I'm currently on my second Chill Mat. The first one died when the connector in the mat went loose, so that the cable's connector no longer made a good connection with it. I sent that one in for warranty service, and got back the one I was using up until a few days ago. The way this one died is that the inline power switch on the cable is now permanently off, and just flops around; I cannot turn it on.

Looking at the website in preparation for this post, I noticed that the Chill Mat shown on the website looks different from mine. Maybe they've redesigned it, and the hardware is more reliable now. I intend to investigate this.

But, if that fails, I have three choices:

  1. Send this thing in for warranty service again.
  2. Surgically remove the inline switch, and hope the connector doesn't flake out.
  3. Get a different active-cooling pad.

I'm seriously considering #3, but I'm not sure of my options. (I am definitely not open to passive-cooling solutions such as Targus' coat rack.) Having made one bad choice before, I now turn the decision over to you.

Can you recommend a good USB-powered active-cooling pad for a 15″ MacBook Pro?

“Photoshop sucks” updated

 

2008-03-23 17:33:51 UTC

Upon inspiration by a comment, I've just updated my rant from a couple years ago, “Photoshop sucks”, to include a list of alternatives. Topping the list, of course, is Acorn; also included are Core Image Fun House, Pixelmator, DrawIt, and Iris.

I'm very glad that there are now solutions to the problem that is Photoshop. I dislike bitching about something without a solution to offer; now I have six to offer, so that rant is now complete.

Apple Bug Friday! 75

 

2008-03-21 19:44:43 UTC

This bug is TN2083 doesn't document all known Session types. It was filed on 2008-01-19 at 16:44 PST.

Read the rest of this entry »

UTI Property List Helper 1.0

 

2008-03-21 10:33:15 UTC

For a forthcoming blog post, I was recently writing a test app that displays images. This had me once again slogging through the list of System-Declared Uniform Type Identifiers and assembling type declarations for the image types by hand.

I'm tired of doing that. So I wrote a new application called UTI Property List Helper.

The main window consists of a table view, in which you enter UTIs, and two text views displaying chunks of Info.plist XML data.

This application automatically updates the CFBundleDocumentTypes and UTImportedTypeDeclarations arrays in its window with information on the types you enter, obtained from Launch Services' database of all types registered by any app on your system.

No more searching for that OSType, or leaving it out because it's too much work—this app does all the grunt work for you. Enjoy.

.hgignore for Mac OS X applications

 

2008-03-20 05:37:06 UTC

If you use version control (and you should), then you're familiar with the pollution that an Xcode build folder can put into your status output:

? build/.DS_Store
? build/Debug/UTI Plist Helper.app/Contents/Info.plist
? build/Debug/UTI Plist Helper.app/Contents/MacOS/UTI Plist Helper
? build/Debug/UTI Plist Helper.app/Contents/PkgInfo
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/InfoPlist.strings
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/MainMenu.nib/info.nib
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
⋮

Good version-control systems offer a way to ignore any file that matches a certain pattern. In the case of an Xcode project, you want to ignore the build folder and a few other things: .DS_Store files, backup nibs (those Foo~.nib packages that IB creates when you save), etc.

In Mercurial, the way to do that is to create a .hgignore file, and populate it with the patterns you want hg to ignore.

In order to save you repetitive work, here's a .hgignore file, already fully-populated, that you can use when versioning your Xcode-based project with Mercurial:

File: hgignore.bz2

syntax: glob

.DS_Store

*.swp
*~.nib

build

*.pbxuser
*.perspective
*.perspectivev3

What to do with this file

  1. Download it, and save the .bz2 file somewhere such as your Documents folder.
  2. cd into the top level of a repository.
  3. Extract the file using this command line: bunzip2 < ~/Documents/hgignore.bz2 > .hgignore
  4. Add the file: hg add .hgignore
  5. Commit it.

Thereafter, not only do you have a .hgignore file keeping your status output clean, but it's versioned, so it's easy for you to track and revert changes to the ignore file over time.

A public thank-you

 

2008-03-17 00:07:16 UTC

This goes out to whichever engineers at Apple fixed the Image Unit template to not suck.

Before Xcode 2.5, that template was useless. Now, it contains everything I need already set up, to the maximum extent possible.

Thank you, Apple engineers.

The Sinister Divide

 

2008-03-16 15:02:07 UTC

In Dvorak, the Sinister Divide is between y and f, between i and d, between k and x, and between 6 and 7.
In QWERTY, the Sinister Divide is between t and y, between g and h, between v and b, and between 6 and 7.

UPDATE: Sören Nils Kuklau, in a tweet, pointed me to another person's blog post about these people, which tells of a C&D letter they received from Domain Registry of America. Not sure what I'll do if I get such a nastygram.


When you own your own domain name, you tend to get shit like this in the mail:

The lowest third of a letter from “Domain Registry of America”, looking very much like a bill.

That's actually the lowest third of a folded letter. So when you first take it out of the envelope, it looks like a bill. Only when you unfold it do you see that it's not quite a bill:

The full letter from “Domain Registry of America”.

I think that they intend for you to see the looks-like-a-bill part of it, conclude that it's a bill, and pay it—and thereby switch your domain name registration over to them.

The back of the letter contains the “registration agreement”:

Paragraph after paragraph of legalese.

I didn't bother to read all of that text (I have no intention of agreeing to it anyway), but I did notice this bit at the top:

This Registration Agreement (“Agreement”) sets forth the terms and conditions of your use of domain name registration and related services (“Services”). In this agreement “you” and “your” refer to you and the registrant listed in the WHOIS contact information for the domain name. “We”, “us” and “our” refer to eNom, Inc., Wild West Domains, Inc., BRANDON GRAY INTERNET SERVICES INC. (dba “NameJuice.com”), and DROA

Emphasis mine. So not only are these people trying to bullshit their way into being my registrar, but they're not even a registrar—just a reseller for eNom, and two other companies I've never heard of.

By the way, the type in that agreement is really freaking small. I measured the capital A at the end of that first paragraph, and found that it's exactly 1 mm tall.

So whenever you get anything in the mail about your domain name, read it VERY thoroughly. Especially don't trust anything that doesn't come from your registrar.

Multiple methods found

 

2008-03-14 04:56:18 UTC

I wrote some code like this:

[[QTMovie movieNamed:sound error:NULL] play];

and Xcode gave me three warnings:

  • GrowlApplicationController.m:537: warning: multiple methods named ‘-play’ found

    • /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSSound.h:47: warning: using ‘-(BOOL)play
    • /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/QTKit.framework/Headers/QTMovie.h:340: warning: also found ‘-(void)play

All warnings are bad, but these are even worse: The compiler is telling me that it's picked the wrong method. I'm creating a QTMovie, so I want it to use -[QTMovie play] (the one that returns void), but it picked -[NSSound play] (the one that returns BOOL).

Now, in this case, it won't matter, thanks to Obj-C's dynamism—all it will do is send a play message to the object, and the object will use whatever play method it knows about. In other words, despite what the compiler is telling me, the program will actually call the correct method (the one in QTMovie).

But this could be a lot worse. If I'm expecting, say, a structure type, and the compiler picks a method that returns a scalar type such as int or float (or vice versa), the compiler will write the wrong assembly code for the message. My method will get gibberish back from the method it called, and Bad Things will follow.

The reason for this  problem is that +movieNamed:error: returns an id—not a QTMovie * specifically. For all the compiler knows, that method returns an NSSound *, or even an NSUserDefaultsController *.

Since the compiler doesn't know what type the method returns, it must pick one from all the methods by that name that it knows about. You can't rely on it picking any particular one, and I could only guess as to how it makes its choice. For all I know, it's blind chance.

The ugly and naïve solution would be to tell the compiler what type we're expecting by casting it:

[(QTMovie *)[QTMovie movieNamed:sound error:NULL] play];

Of course, besides being ugly, this specific code still has the problem of not accepting an error object. What I should do, and will do, is accept the error object and stash the returned movie in a variable, so that I can test it and then either report the error or play the movie.

NSError *error = nil;
QTMovie *movie = [QTMovie movieNamed:soundName error:&error];
if (movie)
    [movie play];
else
    [NSApp presentError:error];

This code is longer, but it will always use the correct method and it doesn't ignore the error. (You may also have noticed that I change the previously-misleading name of the name variable from sound to soundName.)

You have a puzzle. When you run hg st, it says one of your files is modified:

hg st                                                              %~/Python/run_tests(0)
M test.py

But when you run hg diff, it doesn't say anything about the file:

hg diff test.py                                                    %~/Python/run_tests(0)
___
                                                                   %~/Python/run_tests(0)

The reason, at least in my case, was that the file's permissions had changed. hg st acknowledges this change exactly the same way it acknowledges a change to the file's contents, but hg diff only shows changes to the contents of the file, not the metadata. Thus, a metadata change only gets reported by hg st and not by hg diff.

While I prefer hg over svn, this is one advantage that svn has over hg. hg only uses one column to indicate the type of change, and shows the same letter (M) for metadata changes as for content changes. svn, on the other hand, uses seven columns, and a metadata change puts the M in a different column.

There's no way to make hg st use the svn st format, but you can make hg diff show metadata changes. The way to do this is to edit your hgrc file and enable git mode:

[diff]
git=True

There are two hgrc files; you can choose to change either or both. You can edit ~/.hgrc (this is what I recommend), or you can edit the per-repository .hg/hgrc file. (There is no .hg/hgrc file by default, so if you haven't created one already, you would need to create it.)

The difference, as you've probably guessed, is that ~/.hgrc sets hg's default for all repositories, whereas the per-repository hgrc changes the setting only for one repository, overriding ~/.hgrc and hg's own defaults.

Once you make this change, the output from hg diff will include metadata information:

hg diff test.py                                                    %~/Python/run_tests(0)
diff --git a/test.py b/test.py
old mode 100755
new mode 100644
___
chmod a+x test.py                                                  %~/Python/run_tests(0)

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

 

2008-02-22 23:37:05 UTC

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

Read the rest of this entry »

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

 

2008-02-18 02:00:41 UTC

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

 

2008-02-15 17:10:19 UTC

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

 

2008-02-13 17:20:49 UTC

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.

/[<=>]\{7\}