Scaling, padding, Cocoa, and Bindings

I mentioned a while ago needing to implement batched image processing for my mom’s website that would allow her to easily generate a bunch of images of a certain size, padded with a certain color, for her website.

Besides the problems I mentioned in that post, I soon discovered that wrapping things up for Automator isn’t as easy as it ought to be, because Cocoa automator components only support a couple of data types, none of them the ones I needed to handily read in image files, pass them through Quartz Composer compositions, and then write the results back to the file system. I even had a helpful post from an Apple employee on one of the mailing lists, but in the end decided it would be simpler and I’d get better performance out of the result by just writing a custom application.

I pulled my copy of “Cocoa Programming for Mac OS X”, a book by Aaron Hillegas that I recommend for anyone wanting to learn general Cocoa programming. Then I dove right in to Interface Builder to design the interface and help me flesh out the functional spec. It was pretty easy to do that part, and I ended up with a short list of things I needed to do:

  • Bind user interface elements to user defaults, including non-string things like NSColor
  • Verify desired image sizes are valid
  • Choose directories for input files and output files with a file browser
  • Verify the input files are valid image files that can be read by Quartz
  • Load the image files
  • Scale them to a certain size, preserving their aspect ratio
  • Pad them to be square
  • Save them back to disk
  • Display a modal sheet with a progress panel, also using bindings

I learned a lot doing this project: I experimented with several of the graphics APIs, including Core Graphics, Core Image, and NSImage; I used bindings for something other than a spiffy but meaninglessly simple IB demo; and I handled the user interface nicities often not handled by example code. Did I mention this app is also fast? I’ve also accomplished the same task with Graphic Converter that I’m solving here with my own app, and mine is much faster at generating thumbnails.

If you’re interested, you can download the pre-built application or visit my subversion repository for the source code, which I’ve put under a BSD license.

I’ve really enjoyed the graphics programming, and will probably pick up Quartz 2D Graphics for Mac OS X(R) Developers and Programming with Quartz.

Tagged | Comments Off on Scaling, padding, Cocoa, and Bindings

Cocoa Bindings are awesome, yet fraught with peril for the inexperienced

Apparently ivars that are bound to from a nib, via a controller object, don’t have an initial or default value unless I assign one either via code or via the UI element.

Almost all of my experience with the NeXT and Apple APIs pre-dates bindings and core data by nearly a decade, but I’ve had enough of the kool-aid to know the lines of code I never write are much less likely to have bugs than the lines I do write.

Lately I’ve been working on a couple of one-off apps for my personal use, and also to help my mom with gallery images for her website. In the process I’m learning that the practice of bindings is a bit more complicated than the theory of bindings.

For example, I’m working on a simple application that takes a directory full of images, scales each one to a particular square dimension, and pads the shorter side with a user-chosen color. In the NIB for this app, I have an NSTextField for the target dimension and an NSColorWell for the background color.

I’ve discovered that when I launch the app, those IB elements that are bound through an NSObjectController to ivars in MyDocument don’t have an initial default value! For example one of my ivars is *backgroundColor, and the color well is bound to it through the NSObjectController. If I try to read that value from another method in MyDocument without first setting it either via code, e.g. backgroundColor = [NSColor colorBlack], or via the NSColorWell UI element itself, the ivar will be nil! I spent half an hour this afternoon using GDB to track this backwards through my set of Core Image filters before I finally figured it out.

Anyone know how to fix this? I see Core Data has a willReadValueForKey: but I don’t know if it would do the trick.

Tagged | 1 Comment

Big D.I.Y. Egg

bbum’s blog was my first online encounter with the Big Green Egg. Recently he referenced a similar concept, but D.I.Y. instead of purchased. It’s a Terra Cotta Smoker similar to Alton Brown’s, but designed to burn hardwood charcoal instead of using an electric hot plate. The neat thing about this one is I’ve either already got most of the parts for it, or I could acquire them cheaply. I know we already have the biggest part, the large terra cotta pot, because we purchased it several years ago to help heat our oven evenly for making roasts.

Tagged | 1 Comment

QuickTime, Texturing, and OpenGL

The movies I’ve examined don’t have an alpha channel so I’m using RGB and it works fine.

// Create an off-screen gworld where QT can draw
gworldBuffer = calloc(width * height * 3, 1);
QTNewGWorldFromPtr(&gworld, k24RGBPixelFormat, &movieBox, NULL, NULL, 0,
gworldBuffer, movieBox.right * 3);
gworldPixMap = GetGWorldPixMap(gworld);
LockPixels(gworldPixMap);
gworldPixBase = GetPixBaseAddr(gworldPixMap);

And then later on when I want to render to the texture:

// Yield to the movie so it can draw the newest frame
MoviesTask(sMovie, 0);
// The image we get from QuickTime is going to be upside down from the perspective of GL.

// Either render your movie upside down, or use UV mapping in the scene to fix this by flipping the y axis.
glPushAttrib(GL_TEXTURE_BIT);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, movieWidth, movieHeight,
GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)gworldPixBase);
glPopAttrib();

I’m using GWorlds instead of the more modern APIs because my app has to be cross-platform, and there doesn’t seem to be a way to create an OpenGL graphics context on Windows. I’ve filed a Radar bug for this and have hopes it’ll make Leopard or a new release of QuickTime.

If your movie does have an alpha channel or you want to use 32-bit instead of 24-bit textures for some reason, the situation is also pretty easy to handle, though it appears tricky at first because OpenGL wants RGBA or GBRA while the GWorld wants ARGB. Use the k32ARGBPixelFormat when you get your new GWorld, and then later when you render it to the texture, you can use GL_GBRA and tell OpenGL the bytes are reversed, depending on your machine’s byte order:


#if __BIG_ENDIAN__
TYPE = GL_UNSIGNED_INT_8_8_8_8_REV;
#else
TYPE = GL_UNSIGNED_INT_8_8_8_8;
#endif

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, movieWidth, movieHeight,
GL_BGRA, TYPE, (GLvoid *)gworldPixBase);

I tried posting this to the QuickTime developers mailing list in response to a question, but it was bounced by the list administrators because it exceeded the list’s maximum posting size of 12 kilobytes — my post was something like 22 kilobytes with formatting. Horrors! I always wondered why that mailing list was so terse and unhelpful, and now I suspect it’s because people are discouraged from posting examples.

Tagged , | Comments Off on QuickTime, Texturing, and OpenGL

Not Turkey Creek

And not quite Wine Creek, either.

My neighbor and I went on our first mountain bike ride in quite some time. I really enjoy it every time I go, but for some reason my perceived effort from desire to ride to actually riding seems higher with mountain biking than with road biking. That’s partially due to inexperience, so I just need to ride more!

I certainly can’t blame the adventures of this ride on poorly adjusted equipment, but the adjustment on my rear derailer is completely botched. The limits seem reasonable because I can easily switch to the highest and lowest gears on my cogset, though to be fair shifting in to the easiest gear seems more difficult than it should be, but in between easiest and most difficult, the gears jump around crazily any time they’re under load. The limit screws seem to be okay – according to Sheldon Brown’s derailer adjustment guide I should check to see if I bent the derailer or the hanger during one of the tumbles I took on that ride, or the previous time I was off-road, on FATS.

No, our problems on Sunday’s ride were completely due to me not carefully reading the directions to the trail head, leading us to ride on Modoc Trail, called out on the SORBA CSRA site as one of the most technical trails in the area, rather than on Turkey Ceek, which is supposed to be pretty easy.

Sorry, Jeff!

Tagged | Comments Off on Not Turkey Creek