The Cookie Monster From 1967
2007-04-30

Known then as "Arnold The Munching Monster", the Cookie Monster meets (and eats, of course), a talking machine. From an IBM training video, apparently.
|
Aperture: Tips From Bakari Chavanu
2007-04-27

Bakari Chavanu is a wedding photographer who posts Aperture tips as short Quicktime movies on his site. The items covered include organizing folders for wedding photography, using full screen mode, and using the tooltips. There is also an iTunes podcast link and an RSS link.
Aperture Plugin: Improving Image Table Performance
2007-04-26

My first attempt at providing table thumbnails was to load an NSMutableArray with all the images that the user has selected when the plugin starts. Unfortunately there can be a significant delay while they are retrieved from Aperture: above a few hundred the delay becomes irritating. Once the data was in my array, access was fast and the table scrolled up and down smoothly.
To improve start-up performance I replaced my array ivar imageTable with two methods: -countOfImageTableData and -objectInImageTableDataAtIndex:. Now, through the magic of KVC the array controller instantiated in the nib will access these two methods instead of the array. In fact it was trying to do that all along, failing, and falling back to accessing the ivar directly. The idea is that by generating the data on demand, the start-up performance problem will go away and the time taken distributed across all the images as they are viewed in the table.
Here is the code for the first method:

The check for the API version exists because my code simply does not support it. There used to be a less efficient way to retrieve thumbnails that I have no intention of coding. The result of this is that the image table is blank if an old API is detected. I also log a message to that effect on initialization, so it is not a complete mystery to the user.
The second method is very simple:

The code for -textForImageAtIndex: and -thumbnailForImageAtIndex: is not simple, however. But it is straight forward. The text code gets the properties of the image from Aperture and formats it appropriately. The thumbnail code is much easier to follow:

Master images (and potentially any image) don't have thumbnails, so I have to substitute. I do that with a small TIFF image that I put into the plugin bundle that says "No Image".
How did I do? Start-up was fast, even for thousands of images, so that problem was fixed. But there were other problems. Scrolling was dog slow. Logging showed that -countOfImageTableData and -objectInImageTableDataAtIndex: were being called for practically every pixel of scrolling I was doing, retrieving the image and generating the text each time. Horrible.
So the next step was to implement caching: retaining the images and text in memory and delivering those instead of getting them from Aperture each time they are needed.
The other parts of this series can be found via the Cocoa page.
HUDs Just Like Aperture's
2007-04-26
RapidWeaver 3.6 Beta
2007-04-25

The application that I use to create and publish this site is called RapidWeaver, currently at 3.5.1; but there is a new version on the way. I have been testing a beta of RapidWeaver 3.6 and it is looking pretty good. It's stable enough that I use it to publish now. My site is large, and I am running into problems with the current version because of its wasteful use of memory during export. 3.6 fixes that, so my workflow is to create in 3.5.1, save, and then publish using the 3.6 beta.
I use RapidWeaver because it is just so darn easy to use. I only have to touch HTML and CSS if I want to. Site templates are plentiful and low cost and available from a number of third parties. And there is a developer SDK that lets third parties write plug-ins. The only problems I have are self-imposed: my site has 130 pages or so -- and those are actual pages, not the automatically-generated ones line the permalinks. My home page has more than 450 articles. There are at least 500 images in the Aperture section alone, so probably close to 1000 in total now.
But RapidWeaver is keeping up. Not bad for a piece of software that costs $40 and has had free updates for at least a year and a half. I'm eagerly awaiting the release of 3.6. I know what is in it, but you'll have to wait to find out.
[Update: The Realmac Software blog page is now (2007-05-02) starting to reveal some of the new features]
Aperture Plugin: Implementing A Table Of Images
2007-04-22

At this point in the project I decided two things: that I was going to release the plugin as 1.0 in a month, and that I needed to change the interface. I had already switched from a vertical arrangement with the prefix on top, to a horizontal arrangement with the prefix on the left. The trigger for this next change was the unsatisfactory display of the example random filename and the need to distinguish between what happens to the random naming when the freeze feature is used and is not.
The final version I decided to go with features a table that shows a thumbnail, plus the image version name, the image caption, and the new random name. This is the clearest way to show what is going to happen: show it happening.

By using the propertiesWithoutThumbnailForImageAtIndex: and thumbnailForImageAtIndex: methods I can get the data I need from Aperture for each table entry. So my plan was to bind the table to an array controller and bind the array controller to an array in the Random_Wok object that contained all the thumbnails and text. Updating the thumbnails and text would cause changes to propagate through the controller to the view as needed.
So I added an array controller to the nib and called it ImageTable:

I then bound it to the model like this:

and set up like this:

The model key is imageTableData and I use the keys thumb and text to access the data for the columns. So to support the needs of the array controller I implement an array called imageTableData as an ivar in Random_Wok and fill it with dictionaries with keys thumb and text, corresponding to NSImage and NSString objects I want to display.
I set up the table view like this:

Notice that I actually used a custom class for this. More on that later. The first (image) column is set up like this:

To display the image, I drag in an image cell. The properties of the image cell is accessed via the small triangle top right:

The inspector shows it as an NSImageCell, set up this way:

Now onto the bindings. The first column is bound to the array controller and uses the thumb key to get data from the model:

The second column is set up this way:

And bound to the text key through the array controller like this:

As I discovered while attempting to bind each table column to a separate array, setting up table column bindings also automatically sets up the the table bindings. This makes it impossible to bind separate columns to separate array objects: they have to all go to one object and then use the key path to get data from separate places. This pretty much means that you need an array of dictionaries to drive a table.
To provide data for the model, I loaded all the thumbnails and text into the array during the plugin initialization. Through the bindings I was able to change the dictionary contents and have the table update automatically. This all worked fine for a small selection of images.
However if I selected 500 images, the array would take a long time to fill. Worse, this was happening before anything was displayed (since the array controller was set to prepare content), making it look like the plugin has frozen. And changes to the text caused by changing the parameters for the random file names were also very slow because they too would be performed 500 times on all the items in the array.
So another approach was needed.
The other parts of this series can be found via the Cocoa page.
Aperture: 1.5.3 Now Available
2007-04-20
Aperture 1.5.3 is now available via Software Update or from Apple's Download area. It's 130MB in size.
About Aperture 1.5.3 Update
Aperture 1.5.3 addresses issues related to overall reliability and performance in a number of areas, including:
- Generation of thumbnails for adjusted images
- Entering and exiting Full Screen mode
- Working with large sets of keywords in the Keywords HUD
- Restoring from a vault
Among the specific issues that have been addressed:
- Previews now update properly when images are sent to an external editor.
- Leaf Aptus 22 and Aptus 75 images are now imported with the correct orientation.
- When folders are imported as projects, the folder structure is now correctly preserved when identically named subfolders are included in the hierarchy.
- Reconnecting referenced images that have been externally edited now works more reliably.
- Setting the ColorSync profile in the Aperture Print dialog now correctly suppresses color management settings in the Mac OS X Print dialog.
About Aperture 1.5.3 Update
Aperture 1.5.3 addresses issues related to overall reliability and performance in a number of areas, including:
- Generation of thumbnails for adjusted images
- Entering and exiting Full Screen mode
- Working with large sets of keywords in the Keywords HUD
- Restoring from a vault
Among the specific issues that have been addressed:
- Previews now update properly when images are sent to an external editor.
- Leaf Aptus 22 and Aptus 75 images are now imported with the correct orientation.
- When folders are imported as projects, the folder structure is now correctly preserved when identically named subfolders are included in the hierarchy.
- Reconnecting referenced images that have been externally edited now works more reliably.
- Setting the ColorSync profile in the Aperture Print dialog now correctly suppresses color management settings in the Mac OS X Print dialog.
Failure In A Box
2007-04-20

Information Arbitrage has some perspective and discussion on the Xbox 360 and sales in Japan (and on Microsoft's home entertainment division's performance).
Bottom line, Microsoft needs to take a long, hard look at its gaming strategy - and, in fact, its entire H&E strategy. At what point, regardless of its virtually endless financial resources, does it say "enough is enough." Would we have been better served by returning the extra cash to shareholders rather than investing it in a franchise that seems to have questionable prospects for turning around? These are the kinds of questions Microsoft management should be asking. And hopefully, for shareholders' sakes, they are.
Lack of success is not all that surprising if you look at some of the results of surveys of potential customers in Japan. These originally came from Macromill in January 2007. The graphs below were published on Flickr:

Amazingly the Xbox360 was rejected by 71% and deemed a potential purchase by only 4%. 9.3% new nothing of the Xbox360.The Wii was already ahead of the PS3.

The PS3 doesn't do much better:

And supply is surpassing demand. It's pretty amazing that the PS2 will almost certainly outsell all of them in 2007. My kids still play tons of PS1 games on our PS2 machine. If it broke, we'd get another.
Aperture Plugin: Better Logging
2007-04-18

The logging I have been doing is very primitive. I call NSLog() to see what I want to see and have to manually insert and remove these calls (or mess with comments) to control what is going on. It's time for something better. Looking around, I found a handy logging class at Borkware that does much of what I want called MLog.
It implements a logging system that includes the line number and source file name with each message. This is very useful for understanding what is happening when reading the logs. The change I made to that code was to add control over the logging level.
Here are the definitions for my version of MLog.h. I implement seven levels and add the ability to revert to standard NSLog() calls, or to remove all the logging code completely:

Macros take care of inserting the correct code and extracting the file name and line number from the preprocessor. The class implements two class methods: one for actually logging messages, and one for setting the current log level. The idea of the level control is that the minimum log level can be set either by an environment variable or by code and only messages logged at that level or above will be shown.
The Implementation includes a static variable that holds the current log level:

Initialization is done in the class initializer:

It reads the environment variable MLogMinLevel and uses that to set the initial level. The logging code compares the logging level passed to the method with the current level and ignores those below the minimum:

The level setter code is very simple:

To use the logging, I add lines like this to my code:

and it provides messages that look like this that include the level, the file name and the line number:
Since I provide logging control with an environment variable, I can quite easily create build configurations that behave differently. If I go to the Project menu and select Edit Project Settings I can duplicate the current Debug and Release configurations and make two new ones:

Clicking on on the Build tab lets me set these up:

I edit the preprocessor macros to include the symbols I need to control the logging system. The Release No Logging configuration sets __BTREMOVE-LOGGING, for instance. Once set up I can change the current configuration I want to build and run from the main XCode window just by selecting it:

For this to fully work I edit the custom scripts I added to copy the executable and run Aperture, since the build names have changed and there are more of them.
The other parts of this series can be found via the Cocoa page.
Photo Book Quality
2007-04-17

Mike Franklin has been testing the quality of photo books created by a number of different suppliers: Blurb, Apple, Viovio, and MyPublisher.
Wish You'd Been There -- The History Of Pink Floyd
2007-04-16

Originally broadcast by the BBC in 2002, this excellent two-part radio series about Pink Floyd features the band members, people who worked with them and knew them, and of course their music. It's about three hours in total, presented in Real Audio format in four parts (the last two are in the wrong order) so you'll either need the Real player, or VLC to listen to it.
Dave Gilmour hasn't done too badly out of the whole thing either, as a tour of his floating studio on the Thames shows in an episode of the BBC's Three Men In A Boat (Google Video, 5 mins). That link also has many other Pink Floyd clips.
Aperture Plugin: Creating A Universal Binary
2007-04-15

Random Wok is only being compiled for Intel right now. I need a universal binary so that it will run on the PowerPC architecture as well. This is easy to change. I select the Random Wok target:
Then click on the Info button, and under the Architectures tab, select what I need:

That's all there is to it. Everything happens behind the scenes.
The other parts of this series can be found via the Cocoa page.
An Interview With Google CEO Eric Schmidt
2007-04-14
Wired has an interesting interview with Eric Schmidt, taken in 2005, but never published. It includes interesting observations about how they make decisions and deal with conflict:
There was one case in February (2005), which is a very, very difficult issue, and I'd rather not go into the specific product issue, but we were in violent disagreement, and for very good reasons. Actually, Larry and I were in agreement. Sergey was wrong, in my view. Sergey came in and he was just on a tear. He had convinced himself of this and he was absolutely sure. And I said, "Look, I can't take this, guys. You guys have got to sort this thing out. If the two of you can agree, I'll agree with you. But you have a deadline of tomorrow” Again here I am structuring it. "And you don't need to tell me the decision, you have to tell them the decision."
Tell who?
Tell the engineers. It's like 20 people who are completely demoralized because they're not getting clear direction. So what happens is the next night they get to the deadline -- and I remember it because this is an emotional issue -- and I called up Sergey, and I said, "What did you guys decide?" And he said, "I'm going there now to tell them," and he then described the solution, which was different than the three of us did -- and better.
So again the generic model is consensus building with dissent with a deadline. If you don't have dissent, stimulate the dissent, and inspect everything. That's sort of the default model, and then you manage the exceptions.
So the exceptions are when there really is a disagreement among the principles, and certainly I've encouraged people to say “I just don't agree,” and then every once in a while I've had to actually be a real CEO and mandate something and really force it and not listen to anyone else. Those are cases where it's legal or regulatory where I've just said, "Look, I'm just not going to participate in anything other than this outcome." And people know me well enough to know not to challenge me on it.
Tell who?
Tell the engineers. It's like 20 people who are completely demoralized because they're not getting clear direction. So what happens is the next night they get to the deadline -- and I remember it because this is an emotional issue -- and I called up Sergey, and I said, "What did you guys decide?" And he said, "I'm going there now to tell them," and he then described the solution, which was different than the three of us did -- and better.
So again the generic model is consensus building with dissent with a deadline. If you don't have dissent, stimulate the dissent, and inspect everything. That's sort of the default model, and then you manage the exceptions.
So the exceptions are when there really is a disagreement among the principles, and certainly I've encouraged people to say “I just don't agree,” and then every once in a while I've had to actually be a real CEO and mandate something and really force it and not listen to anyone else. Those are cases where it's legal or regulatory where I've just said, "Look, I'm just not going to participate in anything other than this outcome." And people know me well enough to know not to challenge me on it.
Computational Photography
2007-04-10

The 3D image on the right was constructed by a computer from the flat photo on the left (it's just a normal photo, but taken with mirrors). Computational photography does this and much more, as described in an article in Science News.
Aperture Plugin: Automating Builds And Using The Debugger
2007-04-08

So far in this project all my debugging has been done with NSLog() calls since the code is pretty simple. To run my plugin each time I have been dragging the binary from the Build folder to Aperture's export plugins folder, launching Aperture, and then selecting Random Wok from the File > Export menu.
So how about debugging with the debugger? If I do a debug build, go through the same steps, and the run Aperture, my breakpoints are never hit. What is going on?
This is happening because the application, Aperture, is not being run by the debugger, and so my plugin is not being run by the debugger. To make XCode run Aperture I modified the instructions I found in a technical Q & A on Apple's developer site that shows how to handle this situation with a Web Kit plugin. In my case I create a new custom executable in the Projects folder on the left side of the XCode window and set it up this way:

Then I make sure that my build options for the debug build are set correctly: no optimization, generate all symbols, don't strip:

Now I can set breakpoints and have them hit:

I still have to copy the executable and run Aperture manually. But there is a way to fix that. I add a new run script :

And set it up like this:

The debug version is set up with a symbolic link and the release version with a copy. Here is the full text:
# clean up any previous products/symbolic links in the target folder
if [ -a "${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}" ]; then
rm -Rf "${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}"
fi
# Depending on the build configuration, either copy or link to the most recent product
if [ "${CONFIGURATION}" == "Debug" ]; then
# if we're debugging, add a symbolic link to the plug-in
ln -sf "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" \
"${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}"
elif [ "${CONFIGURATION}" == "Release" ]; then
# if we're compiling for release, just copy the plugin to the Internet Plug-ins folder
cp -Rfv "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" \
"${USER_LIBRARY_DIR}/Application Support/Aperture/Plug-Ins/Export/${FULL_PRODUCT_NAME}"
fi
Here is how the debug version looks in the Export folder:

Now when I compile and run or compile and debug, the script is run and Aperture is launched. Any breakpoints I have set work.
The other parts of this series can be found via the Cocoa page.
Frasier Spiers Talks About The Aperture Export API
2007-04-07

Late Night Cocoa has just published a podcast featuring Frasier Spiers. He talks about the Aperture export plugin API.
Vista Is Still Lagging Windows 2000 5 to 1
2007-04-07
The OS stats for this site show that Vista is not yet as popular as Windows 2000. I find that quite surprising, but that's what it says:

The site looks fine in Explorer 7 (checked here). Maybe the search on Vista defaults in such a way that this site is not found. Or maybe it's impossible to use a camera with Vista and so nobody is.
The browser share tells a different story:

Netscape stalled at version 5. Explorer 7 is being adopted pretty quickly. Everyone uses Firefox. Tiger has taken over completely.

The site looks fine in Explorer 7 (checked here). Maybe the search on Vista defaults in such a way that this site is not found. Or maybe it's impossible to use a camera with Vista and so nobody is.
The browser share tells a different story:

Netscape stalled at version 5. Explorer 7 is being adopted pretty quickly. Everyone uses Firefox. Tiger has taken over completely.
Subversion Part 4: Creating A Release
2007-04-06

Now I have my repository set up, my code imported, and a working copy checked out that I am doing development on, I am going to release a snapshot of the current code as a beta, since I want some people to test it. In order to be able to keep working on the main code (in trunk) while I wait for feedback from testers I have to fork my code. If the testers find a problem and I fix it, I want to send out just the fix, not the current development version with the fix. Later I can merge the fix back into my main code.
Using subversion I do this by copying my code base in the trunk releases virtual folder into a new folder. After making sure that my project is checked in, I open svnX and make sure the correct revision is selected in the top pane. By selecting trunk:

and clicking on the svn copy button, I am given an opportunity to name and locate my new virtual folder:

I select branches as the location and fill in a name for the target of the copy. I add a message and hit commit:

And there is the new release in the wrong place. I meant to put it in releases. No matter. I just use the svn move button to move the beta1.0 folder to the right place:

Done. Now to work on the beta I check it out, or I can continue working on the trunk. If I check it out I have to create a set up a new build folder for it of course. Here is the current folder tree:

The other parts of this series can be found via the Cocoa page.
Aperture Plugin: Displaying The Image Count With Bindings
2007-04-05

To give some feedback to the user I want to include a display of the number of images that will be exported. I can do that by using bindings: by binding text on the window to an ivar in my Random_Wok object.
I create an ivar called imagesToProcess and an accessor to set it:

In -willBeActivated, I add some code to set it up before it is used:
And since changes in the image type (master or version) can change the number of images, I have to set it each time the export type changes:

That is all the code except for this method:

It returns "s" if imagesToProcess is more than one, otherwise an empty string. I need that in order to implement correct pluralization of my display string.
To display the image count on the window I add an NSTextField in the corner like this:

Its value is unimportant because I will be constructing it dynamically with bindings, but it helps to have a descriptive string there. I set up its bindings like this:

The Display Pattern string is what does the magic. Value1 and Value2 are bound to different key paths. The first to the value given by imagesToProcess, and the second to the plural string given by pluralImagesToProcess:

When these are substituted into the Display Pattern string, the result is what the user needs to see how many images are selected:

Because I implemented the accessors and I use them to change the value, bindings take care of doing this display updates automatically. If I select images in Aperture that include some with multiple versions of one master, the image count displayed changes when I click Master and then Version, just as it should.
The other parts of this series can be found via the Cocoa page.
Aperture: End A Show Of A Selection Of Photos With A Blank Screen
2007-04-04
I often use full screen mode with no visible filmstrip to show photos to people, clicking through them one at a time with right-arrow. It's quick because I don't have to wait for previews to render (necessary to use the built-in slide show feature of Aperture), but there are some disadvantages to doing things this way. First, I have to show all the images in the album or project: I can't make a selection. Second, at the end of the photos there sits the last one on the screen: no blank screen.
To fix both of these problems, I use command-click. Here's how:
• Make sure the display is in Primary Only mode (option R). This makes the viewer show only one of the selected images at once.
• Select images using shift-clicking and command-clicking until the selection is as desired
• Go into full screen mode with F
• The first selected image will be displayed -- command-click it
• The next image is displayed. Keep command-clicking until all images have been displayed. On command-clicking the last one, a blank screen will appear.
[Added from the comments:]
Add an image of your logo (or URL or whatever) to the album or project. It doesn't matter where it is in the browser, as long as you click on it *last* to add it. Then click on the first image you want to show and hit F. Now the last image to be shown will be the logo.
Using the method of display above, images are shown in the order added to the selection, so you can not only choose what to show, but the order as well and very easily.
To fix both of these problems, I use command-click. Here's how:
• Make sure the display is in Primary Only mode (option R). This makes the viewer show only one of the selected images at once.
• Select images using shift-clicking and command-clicking until the selection is as desired
• Go into full screen mode with F
• The first selected image will be displayed -- command-click it
• The next image is displayed. Keep command-clicking until all images have been displayed. On command-clicking the last one, a blank screen will appear.
[Added from the comments:]
Add an image of your logo (or URL or whatever) to the album or project. It doesn't matter where it is in the browser, as long as you click on it *last* to add it. Then click on the first image you want to show and hit F. Now the last image to be shown will be the logo.
Using the method of display above, images are shown in the order added to the selection, so you can not only choose what to show, but the order as well and very easily.
Site Focus: The Hyperjeff Network
2007-04-03
It's hard to describe The Hyperjeff Network succinctly. It's a Cocoa site with a huge amount of material. There are links to 17,436 native OS X applications, many with full source. There is an entire page of links to OS X-centrix sites. And don't miss the list of 361 links to articles about programming in Cocoa. Aperture Plugin: Making A Customized Button
2007-04-02

The next feature I want to add is a Bagelturf badge that can be clicked to launch a browser to my home page. For that I need a graphic and some way of making a click open a URL. Since the badge won't obviously be a button, or have a button action, I want the cursor to change to a pointing hand to show that it is clickable.
After a lot of messing about with tracking rectangles I discovered that the correct way to implement the pointing hand was by using -resetCursorRects. I have to subclass NSButton and then override -resetCursorRects to define the cursor shape that I want when the cursor is inside my button.
First I drag a button in Interface Builder onto my window and select the Rounded bevel Button type. Then I create a new file and declare it as a subclass of NSButton using this code:

Then I add the implementation code:

To use the custom class in my nib file, I save those files and drag the .h file onto my nib file. This makes the nib file aware of the custom class and selects the class:

Now I can select my button and set its custom class to BTPointingHandButton:

By making a small image in Photoshop that has transparent background and saving it as a TIFF, I can have the image lay on the window background. To add the image to my XCode project I drag it onto the Resources folder and opt to copy it in.

Clicking on the image and using the inspector shows me what is there:

By dragging the image onto the button, the image is automatically set:

And finally I can add an action to File's owner via the attributes pane called bagelturfAction and hook it up to the button with control-drag. To make the action open a URL I add the action code to Random_Wok.m and use NSWorkspace's openURL method:

Done. Now if I hover over the button the cursor changes and a click launches Safari and goes to my home page.
The other parts of this series can be found via the Cocoa page.
The Bagelturf site welcomes Donations of any size
