<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9199573357345606334</id><updated>2012-01-03T14:31:45.092-08:00</updated><category term='cocoa bindings'/><category term='fundraiser'/><category term='cancer'/><category term='WWDC'/><category term='meetup'/><category term='workflow'/><category term='refactoring'/><category term='image unit'/><category term='transition'/><category term='nda'/><category term='livestrong'/><category term='iphone sdk'/><category term='san francisco'/><category term='bug'/><category term='big nerd ranch'/><category term='justice'/><category term='cifilter'/><category term='core image'/><category term='mac os x'/><category term='ipad'/><category term='QCView'/><category term='linkedin'/><category term='CALayer'/><category term='demo'/><category term='DEVONagent'/><category term='bindings'/><category term='sting'/><category term='cocoa'/><category term='applescript'/><category term='script editor'/><category term='iPhone'/><category term='quartz composer'/><category term='convolution'/><category term='ios'/><category term='charity'/><category term='Core Animation'/><category term='cycling'/><category term='open gl'/><category term='UITableView'/><category term='gui scripting'/><category term='image processing'/><category term='uikit'/><category term='commuting'/><category term='training'/><category term='automator'/><category term='google'/><category term='lance armstrong foundation'/><title type='text'>From a remote village</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>23</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-3519435369133648107</id><published>2011-08-27T12:25:00.000-07:00</published><updated>2011-08-29T10:30:57.962-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='ios'/><category scheme='http://www.blogger.com/atom/ns#' term='uikit'/><category scheme='http://www.blogger.com/atom/ns#' term='Core Animation'/><category scheme='http://www.blogger.com/atom/ns#' term='ipad'/><title type='text'>Core Animation Part 2: View based animation in UIKit</title><content type='html'>&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Animating UIView Properties&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Several view properties are animatable. They are the view's &lt;b&gt;frame (&lt;/b&gt;the view's dimensions in its parent's coordinate space), its &lt;b&gt;bounds&lt;/b&gt;(its dimensions in its own space), the &lt;b&gt;center&lt;/b&gt; of the view, the &lt;b&gt;transform&lt;/b&gt; (2D transforms only), the &lt;b&gt;alpha&lt;/b&gt;, or transparency, the &lt;b&gt;backgroundColor&lt;/b&gt; and the &lt;b&gt;contentStretch&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Most of these properties are straightforward. The really interesting one is the&lt;br /&gt;transform. The transform property allows you to apply an &lt;a href="http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_affine/dq_affine.html#//apple_ref/doc/uid/TP30001066-CH204-TPXREF101"&gt;affine transform&lt;/a&gt; to&lt;br /&gt;the view. This is really powerful. Generally, affine transforms allow you to scale a view (change its size), translate a view (change its location), or rotate a view, or any combination of the three. For UIView animations you use the &lt;a href="http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html"&gt;CGAffineTransform&lt;/a&gt; structure. If you have a view and you want it to appear 50 percent smaller on the screen, you would simply:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;view.transform = CGAffineTransformMakeScale(0.5, 0.5);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will shrink the view 50 percent in each direction. It does this without changing the actual size of the view as far as the view is concerned. If you want to scale the view down by 50% and also rotate it 75 degrees you simply:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;CGAffineTransform scale = CGAffineTransformMakeScale(0.5,0.5);&lt;br /&gt;CGAffineTransform scaleAndRotate =&lt;br /&gt;    CGAffineTransformRotate(scale, 75.0 * M_PI / 180.0);&lt;br /&gt;view.transform = scaleAndRotate;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thus you can combine multiple transforms together into a single transform. You also need to be careful with animating the frame if you have set the transform to anything except for the identity transform. Since I'm using transforms extensively in this app, I'm not going to touch the frame.&lt;br /&gt;&lt;br /&gt;In &lt;a href="https://github.com/PaulMaxime/CADemo"&gt;CADemo&lt;/a&gt; the views are sized to fill most of the screen, with a bit of space around them. The views are positioned in the center. As far as these views are concerned, their size and rotation never change. View transforms like this are only 2D - all rotations are in the plane of the screen. If you want 3D rotation of your views, you'll have to step down into direct Core Animation.&lt;br /&gt;&lt;br /&gt;Another interesting point is that the views can still interact with the user in their transformed state. You don't have to write a single line of code. The system knows how to deal with the transforms of views and makes sure that each view receives events in the coordinate space that it expects.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;View Layout in CADemo Using View Animations&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;You can use these simple view animations to do some fun and surprisingly sophisticated things. For example, in &lt;a href="https://github.com/PaulMaxime/CADemo"&gt;CADemo&lt;/a&gt;, there are two different layouts (grid and circle) for the main screen. These views have some simple code to determine their layout. This code is defined in &lt;a href="https://github.com/PaulMaxime/CADemo/blob/master/CADemo/CardLayoutView.m"&gt;CardLayoutView.m&lt;/a&gt; in the demo project. The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-updateCircle&lt;/span&gt; method computes the layout of the views in a circle. The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-updateGrid&lt;/span&gt; method does the same thing for the grid.&lt;br /&gt;&lt;br /&gt;The grid layout is a flexible grid which dynamically computes the size of each subview, based on the number of rows and columns desired, the spacing between the views, and the inset of the grid from the edge of the screen. We do some simple math:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)updateGrid {&lt;br /&gt;  CGRect bounds = self.bounds;&lt;br /&gt;&lt;br /&gt;  subviewSize_.width = (bounds.size.width - (2 * inset_.width) -&lt;br /&gt;                       ((columns_ - 1) * spacing_.width)) / columns_;&lt;br /&gt;  subviewSize_.height = (bounds.size.height - (2 * inset_.height) -&lt;br /&gt;                        ((rows_ - 1) * spacing_.height)) / rows_;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll get back to this in a moment in more detail, but here we start our animation transaction, set the animation curve (ease in/out starts and ends slowly), and the duration of the animation.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[UIView beginAnimations:@"gridView" context:NULL];&lt;br /&gt;  [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];&lt;br /&gt;  [UIView setAnimationDuration:animationDuration_];&lt;br /&gt;&lt;br /&gt;  NSUInteger index = 0;&lt;br /&gt;  NSArray *newViews = [self viewsFromDataSource];&lt;br /&gt;  for (DemoCardView *view in newViews) {&lt;br /&gt;    if (!view.isZoomedIn) {&lt;br /&gt;      NSUInteger row = index / columns_;&lt;br /&gt;      NSUInteger col = index % columns_;&lt;br /&gt;      row = row % rows_;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For each view index, I compute the row and column for that view. for this demo,&lt;br /&gt;all the views fit on a single page, but it's also easy to compute an offset for&lt;br /&gt;paging, which would allow you to have multiple pages of the grid.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// Compute the new rect&lt;br /&gt;      CGRect newFrame = CGRectMake(&lt;br /&gt;          inset_.width + col * (subviewSize_.width + spacing_.width),&lt;br /&gt;          inset_.height + row * (subviewSize_.height + spacing_.height),&lt;br /&gt;          subviewSize_.width, subviewSize_.height);&lt;br /&gt; &lt;br /&gt;      // Use the transform to resize the view. Move it by setting the center.&lt;br /&gt;      CGFloat scale = [GraphicsUtils scaleForSize:self.bounds.size&lt;br /&gt;                                           inRect:newFrame];&lt;br /&gt;      view.center = [GraphicsUtils centerOfRect:newFrame];&lt;br /&gt;      view.transform = CGAffineTransformMakeScale(scale, scale);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here we are moving the view into the correct location and scaling it to fit in the grid.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if (![self.subviews containsObject:view]) {&lt;br /&gt;          [self addSubview:view];&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    index++;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Any view properties set between the +beginAnimations:context: are now animated at the same time.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[UIView commitAnimations];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For the circle layout, we put all the demo views into a circle around the screen. I've taken a few shortcuts here for the purposes of the demo, which you'd want to fix if you ever used this code for a real application&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (void)updateCircle {&lt;br /&gt;  CGRect bounds = self.bounds;&lt;br /&gt;  NSUInteger index = 0;&lt;br /&gt;&lt;/pre&gt;This is a repeat of the animation code in -updateGrid.&lt;br /&gt;&lt;pre&gt;[UIView beginAnimations:@"circleView" context:NULL];&lt;br /&gt;  [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];&lt;br /&gt;  [UIView setAnimationDuration:animationDuration_];&lt;br /&gt;&lt;br /&gt;  // This is pure laziness. I should figure out the correct size based on&lt;br /&gt;  // number of views and the the screen size.&lt;br /&gt;  subviewSize_ = CGSizeMake(175, 175);&lt;br /&gt;&lt;br /&gt;  CGPoint center = [GraphicsUtils centerOfRect:bounds];&lt;br /&gt;  CGSize size = bounds.size;&lt;br /&gt;  CGFloat offset = 0.5 * MIN(&lt;br /&gt;      size.width - subviewSize_.width - inset_.width,&lt;br /&gt;      size.height - subviewSize_.height - inset_.height);&lt;br /&gt;&lt;/pre&gt;The offset computes how far from the center of the screen we want the center of each view.&lt;br /&gt;&lt;pre&gt;NSArray *newViews = [self viewsFromDataSource];&lt;br /&gt;  CGFloat angle = 2 * M_PI / [newViews count];&lt;br /&gt;&lt;/pre&gt;We compute the change in angle by dividing a circle up by the number of views.&lt;br /&gt;&lt;pre&gt;for (DemoCardView *view in newViews) {&lt;br /&gt;    if (!view.isZoomedIn) {&lt;br /&gt;     CGFloat xOffset = offset * cosf(angle * index);&lt;br /&gt;      CGFloat yOffset = offset * sinf(angle * index);&lt;br /&gt;      view.center = CGPointMake(center.x + xOffset, center.y + yOffset);&lt;br /&gt;&lt;/pre&gt;This little bit of trigonometry computes the location of the center of each view, rotated around the center of the screen.&lt;br /&gt;&lt;pre&gt;CGRect newBounds = CGRectMake(0, 0, &lt;br /&gt;          subviewSize_.width, subviewSize_.height);&lt;br /&gt;      CGFloat scale = [GraphicsUtils scaleForSize:view.bounds.size&lt;br /&gt;                                           inRect:newBounds];&lt;br /&gt;      CGAffineTransform scaleAndRotate = CGAffineTransformRotate(&lt;br /&gt;          CGAffineTransformMakeScale(scale, scale),&lt;br /&gt;          angle * index + M_PI_2);&lt;br /&gt;      view.transform = scaleAndRotate;&lt;br /&gt;&lt;/pre&gt;Then we compute the scale and rotation of the view so that it is rotated with the top of the view towards the outside of the circle.&lt;br /&gt;&lt;pre&gt;if (![self.subviews containsObject:view]) {&lt;br /&gt;            [self addSubview:view];&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      index++;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;And commit the animations.&lt;br /&gt;&lt;pre&gt;[UIView commitAnimations];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;I didn't talk about the animation code in here yet. The simple answer is that adding the 4 lines of code from&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt; +beginAnimations:context:&lt;/span&gt; to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;+commitAnimations&lt;/span&gt; is all you need to do to make these animate. When you toggle the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;a href="http://developer.apple.com/library/ios//#/library/mac/documentation/UIKit/Reference/UISegmentedControl_Class/Reference/UISegmentedControl.html"&gt;UISegmentedControl&lt;/a&gt;&lt;/span&gt; in the app's toolbar, the views will smoothly animate between the two layouts. Really, that's all you have to do. View 0 is at the upper left hand corner in the grid view and rotated 90 degrees and all the way on the right side in the circle.&lt;br /&gt;&lt;br /&gt;I don't have to tell that view how to get from the corner to the side. The animation system figures it out for me. If you toggle the grid/circle mode quickly, you'll see that the views stop in the middle of the animation and head off to their new location seamlessly. It's awesome. I recommend setting the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;animationDuration_&lt;/span&gt; variable to something long, like 10 seconds so you can really play around with it.&lt;br /&gt;&lt;br /&gt;One thing I haven't mentioned is that in iOS 4 and above, you should use &lt;a href="http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/clm/UIView/animateWithDuration:animations:"&gt;blocks&lt;/a&gt; instead of the transaction style I'm using here. Blocks are recommended since it's easier to see everything together, and it's also easier to chain a series of animations together. With blocks, animations can be nested as well. I'll show that a little later, this post is getting long. Next time, I finish up with view animations, including animation delegates and blocks.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-3519435369133648107?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/3519435369133648107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=3519435369133648107' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3519435369133648107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3519435369133648107'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2011/08/core-animation-part-2-view-based.html' title='Core Animation Part 2: View based animation in UIKit'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-1521633295475686001</id><published>2011-08-13T13:35:00.000-07:00</published><updated>2011-08-13T13:35:34.874-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='ios'/><category scheme='http://www.blogger.com/atom/ns#' term='Core Animation'/><category scheme='http://www.blogger.com/atom/ns#' term='ipad'/><category scheme='http://www.blogger.com/atom/ns#' term='CALayer'/><title type='text'>Core Animation Part 1: What is Core Animation?</title><content type='html'>Everything we do in user interfaces on our iPhones and iPads, and increasingly on our Macs is animated. Table views slide in and out of view smoothly. Buttons flow between different text and colors, popup views flip on to the screen. An indicator pulses smoothly to alert the user that his download is complete. When the device rotates, screen elements flow into place instead of just jumping jarringly between portrait and landscape.&lt;br /&gt;&lt;br /&gt;This type of animation is more than just "eye candy". It helps the user feel connected to your application. It gives them important clues to how the application is operating, provides valuable visible feedback and projects a sense of high quality design to your user.&lt;br /&gt;&lt;br /&gt;All of this is accomplished using Core Animation. Even if you have no idea what a &lt;b&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CALayer_class/Introduction/Introduction.html#//apple_ref/doc/uid/TP40004500"&gt;CALayer&lt;/a&gt;&lt;/b&gt; is or never typed &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;#import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;&lt;/span&gt;&amp;nbsp; at the top of one of your header files you've used this technology as a developer. Animation is pervasive throughout the &lt;b&gt;UIKit&lt;/b&gt;. &lt;b&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html"&gt;UITableView&lt;/a&gt;&lt;/b&gt;&amp;nbsp;has 11 methods which mention animated or animation, &lt;b&gt;&lt;a href="http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UINavigationController_Class/Reference/Reference.html"&gt;UINavigationController&lt;/a&gt;&lt;/b&gt; has 7. If you've ever pushed a view controller on a stack you've used it.&lt;br /&gt;&lt;br /&gt;One key to understanding Core Animation is to know that it's extremely easy to use. If you've written standard animation code before you have to set up some sort of timer and &lt;a href="http://journals.ecs.soton.ac.uk/java/tutorial/ui/drawing/animLoop.html"&gt;animation loop&lt;/a&gt;. At each time interval, the system will call you back and tell you "Time is now .4 seconds." if you wanted your ball to move across the screen in 1 second, you have to compute the current position as 40 percent of the way between the start and end point, assuming you wanted to use &lt;a href="http://en.wikipedia.org/wiki/Linear_interpolation"&gt;linear interpolation&lt;/a&gt;. If you want a more natural movement, you might choose an &lt;a href="http://en.wikipedia.org/wiki/Cubic_interpolation"&gt;easing function&lt;/a&gt; or some other type of animation timing function that gives more natural movement. Each time you get called you compute the updated position of everything in your scene and draw it. At 30 or 60 frames per second you get smooth motion.&lt;br /&gt;&lt;br /&gt;If you want to do full 3D animation, such as in a game, or in my case, the 3D animated page curl we use in &lt;a href="http://books.google.com/help/ebooks/ios.html"&gt;Google Books&lt;/a&gt;, you will have to do this sort of animation. Core Animation does have some 3D elements but at heart is is a 2D Technology (We might call it 2.5D).&lt;br /&gt;&lt;br /&gt;Throw all that stuff out. With CA, you express &lt;i&gt;intent&lt;/i&gt;. What I mean is that you express the state you want your object to start in, the state you want it to end in, and how long you want it to take. The CA system takes over for you from there. It's also possible to have the animation continue from the current state, so if you are moving something from right to left, and then decide it needs to move back to the right, it will pick up right in the middle of the previous animation. We'll see this in our demo application. The main screen of the app has two view modes - grid and circle, and smoothly animates the position of subviews to the desired end state even if the animation is still in progress. It's really quite amazing and requires no cleverness on the part of the developer.&lt;br /&gt;&lt;br /&gt;The most basic element of Core Animation is the &amp;nbsp;layer (&lt;a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CALayer_class/Introduction/Introduction.html#//apple_ref/doc/uid/TP40004500"&gt;&lt;b&gt;CALayer&lt;/b&gt;&lt;/a&gt;). That layer is then composited on to the screen using the graphics card so that you automatically get hardware accelerated rendering. Layers have animatable properties such as opacity, bounds, position, shadow and more. You animate layers by manipulating these properties, either directly, by attaching a concrete subclass of &amp;nbsp;&lt;a href="http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CAAnimation_class/Introduction/Introduction.html"&gt;CAAnimation&lt;/a&gt;&amp;nbsp;to the layer, or implicitly, by simply setting the value of a property. Within a run loop, these property changes and animations are enclosed in a &lt;a href="http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/Transactions.html"&gt;CATransaction&lt;/a&gt; so that they are all applied together. The animations run in their own thread, so they run without interfering with the rest of your program.&lt;br /&gt;&lt;br /&gt;We'll get back to directly animating layers, the different types of layers and animations and implicit vs. explicit animation in future posts. In the next post, I'm going to talk about how you animate UIViews as opposed to CALayers using &lt;a href="http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6"&gt;view animations&lt;/a&gt;. With nothing more than this, you'll see how easy it is to make some really cool animated user interfaces. We'll get into code as I explain how I implemented the home screen of my &lt;a href="https://github.com/PaulMaxime/CADemo"&gt;CADemo&lt;/a&gt; application. The code's up on &lt;a href="https://github.com/PaulMaxime/CADemo"&gt;GitHub&lt;/a&gt; if you want to follow along.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-1521633295475686001?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/1521633295475686001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=1521633295475686001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1521633295475686001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1521633295475686001'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2011/08/core-animation-part-1-what-is-core.html' title='Core Animation Part 1: What is Core Animation?'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-7937866224566920359</id><published>2011-08-06T17:14:00.000-07:00</published><updated>2011-08-13T13:36:42.309-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='meetup'/><category scheme='http://www.blogger.com/atom/ns#' term='demo'/><category scheme='http://www.blogger.com/atom/ns#' term='Core Animation'/><title type='text'>Introduction to Core Animation</title><content type='html'>I recently gave a talk introdcing the basics of Core Animation at the &lt;a href="http://boulderios.com/"&gt;Boulder iOS Developers Meetup&lt;/a&gt;. I wrote an iPad application that shows off several of the aspects of Core Animation including UIKit animation, Basic Animations, Group Animations, Key Frame animation, Core Animation Transitions, Embedding video in a layer, 3D Transforms, and using Core Animation to build custom user interfaces. &lt;br /&gt;&lt;div&gt;&lt;br /&gt;Here's a screenshot of the app. May not look like much, but each pane of the UI shows a different demo and the entire application itself is implemented using Core Animation.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://1.bp.blogspot.com/-IHz4n5SrLOw/Tj3ZKT_Y1SI/AAAAAAAAASU/aPfnWzleoJg/s1600/CADemoScreenshot.png"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5637901079725790498" src="http://1.bp.blogspot.com/-IHz4n5SrLOw/Tj3ZKT_Y1SI/AAAAAAAAASU/aPfnWzleoJg/s320/CADemoScreenshot.png" style="cursor: pointer; display: block; height: 320px; margin-bottom: 10px; margin-left: auto; margin-right: auto; margin-top: 0px; text-align: left; width: 242px;" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/-nBz3uVe40I0/Tj3aQtQK34I/AAAAAAAAASc/hpq5-N3VUCM/s1600/CADemoCircle.png"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5637902289097908098" src="http://4.bp.blogspot.com/-nBz3uVe40I0/Tj3aQtQK34I/AAAAAAAAASc/hpq5-N3VUCM/s320/CADemoCircle.png" style="cursor: hand; cursor: pointer; display: block; height: 320px; margin: 0px auto 10px; text-align: center; width: 242px;" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;I'll be posting details of each of the demos and the application itself over the next several posts. My code is located on GitHub at &lt;a href="https://github.com/PaulMaxime/CADemo"&gt;https://github.com/PaulMaxime/CADemo&lt;/a&gt; if you'd like to follow along. The next post gives an &lt;a href="http://fromaremotevillage.blogspot.com/2011/08/core-animation-part-1-what-is-core.html"&gt;introduction to Core Animation&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-7937866224566920359?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/7937866224566920359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=7937866224566920359' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7937866224566920359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7937866224566920359'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2011/08/introduction-to-core-animation.html' title='Introduction to Core Animation'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-IHz4n5SrLOw/Tj3ZKT_Y1SI/AAAAAAAAASU/aPfnWzleoJg/s72-c/CADemoScreenshot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-7189444352189572156</id><published>2008-12-02T09:48:00.000-08:00</published><updated>2008-12-02T13:26:19.232-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='livestrong'/><category scheme='http://www.blogger.com/atom/ns#' term='charity'/><category scheme='http://www.blogger.com/atom/ns#' term='fundraiser'/><category scheme='http://www.blogger.com/atom/ns#' term='cancer'/><category scheme='http://www.blogger.com/atom/ns#' term='lance armstrong foundation'/><title type='text'>Riding the Livestrong Challenge. Asking you for a contribution.</title><content type='html'>Hello friends-&lt;br /&gt;&lt;br /&gt;As many of you know my life has been profoundly affected by cancer. Four years ago my dear former wife &lt;a href="http://blueiris.smugmug.com/gallery/1899202_jdmhy#95766440_oWNNs"&gt;Susan&lt;/a&gt; died from metastatic breast cancer after a 15 year on and off battle with the disease. I was with her and held her hand as she passed. Just one year ago, my mother also died due to lung cancer after fighting for a couple of years herself. She had never smoked a day in her life. I know that many of you have faced this terrible disease either yourself or in your family. Everyone is or will be affected by this disease at some point in their life.&lt;br /&gt;&lt;br /&gt;One of my favorite bloggers, &lt;a href="http://fatcyclist.com/"&gt;Elden Nelson  of  fatcyclist.com&lt;/a&gt; is now facing the same battle with his wife also named Susan. His situation is remarkably similar to mine of four years ago. His Susan has had her cancer spread to her bones and her brain and she is on steroids and receiving oxygen. The many rounds of Chemotherapy have failed to eradicate the disease. She is now in hospice care.&lt;br /&gt;&lt;br /&gt;Elden has decided to form a team to join the &lt;a href="http://www.livestrongchallenge.org/site/c.frKPI1PAIoE/b.3920225/k.BDC4/Home.htm"&gt;Livestrong Challenge&lt;/a&gt;. His goal is to raise over 1 million dollars for cancer research. We may not get a cure right away but we can strike a blow. I have decided to join with Elden and his Team Fatty in this year’s event in San Jose California. I plan on riding my bike the full 100 miles. We want to be the largest team in Livestrong history and raise the most money we can.&lt;br /&gt;&lt;br /&gt;As part of this effort I’m asking you to give me a hand at raising as much money as possible. &lt;a href="http://sanjose09.livestrong.org/paulmaxime"&gt;Please consider donating $25 or more to my effort on my personal page&lt;/a&gt;. I am planning on matching whatever amount I get personally, and then my employer, Google, will match me up to $3000. So, you can see that your donation will be multiplied by a factor of 3.&lt;br /&gt;&lt;br /&gt;Thanks for your consideration in helping me in this effort.&lt;br /&gt;&lt;br /&gt;Paul&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-7189444352189572156?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/7189444352189572156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=7189444352189572156' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7189444352189572156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7189444352189572156'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2008/12/riding-livestrong-challenge-asking-you.html' title='Riding the Livestrong Challenge. Asking you for a contribution.'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-1576157912493451541</id><published>2008-10-30T11:31:00.000-07:00</published><updated>2008-10-30T11:35:57.838-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sting'/><category scheme='http://www.blogger.com/atom/ns#' term='san francisco'/><category scheme='http://www.blogger.com/atom/ns#' term='justice'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Police nab bicycle thief thanks to stupidity.</title><content type='html'>This is a little different type of post for me, but last week I had three bicycles stolen from my locked storage unit at my apartment building here in San Francisco. Thanks to some quick thinking by one of Stacie's employees and a bit of finagling on my part, we were able to arrange for the "alleged" thief to get arrested and for me to recover at least one of the bikes. Instead of reposting the whole story I'd like to refer you to my daughter's blog: &lt;a href="http://ilovelladro.blogspot.com/2008/10/sting-at-google.html"&gt;http://ilovelladro.blogspot.com/2008/10/sting-at-google.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-1576157912493451541?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/1576157912493451541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=1576157912493451541' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1576157912493451541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1576157912493451541'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2008/10/police-nab-bicycle-thief-thanks-to-me.html' title='Police nab bicycle thief thanks to stupidity.'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-100394964413353683</id><published>2008-10-12T21:43:00.000-07:00</published><updated>2008-10-12T23:45:24.468-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='UITableView'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone sdk'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><title type='text'>Organizing a complex UITableView with the iPhone SDK by using a dispatch table</title><content type='html'>&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;Hi. I've been working on an iPhone SDK application for a while, and one major piece of this application is a fairly complex table view. The &lt;span style="font-size: small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;UITableView&lt;/span&gt;&lt;/span&gt; is the workhorse class of most iPhone applications - you see it everywhere. One problem with the table view is that the controller class tends to get very large and complex, especially if you have a multiple section table view that has different types of custom cells and behaviors. There are many things that can be done to make this job easier. One excellent technique was recently documented by Frasier Speirs: &lt;a href="http://speirs.org/2008/10/11/a-technique-for-using-uitableview-and-retaining-your-sanity/"&gt;A technique for using UITableView and retaining your sanity.&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;I've developed a technique that can make use of this technique but that goes beyond it by allowing you to delegate the handling of each section in a table view to its own class. It is also easily configurable if you need to add or remove a section type from your controller later, and allows you to localize your changes so that you don't have to remember to modify code in several places.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;As I'm sure many of you know, the &lt;span style="font-family: &amp;quot;; font-size: small; font-size: x-small;"&gt;UITableViewDelegate&lt;/span&gt; protocol contains about 16 methods that take either an &lt;span style="font-family: &amp;quot;; font-size: small; font-size: x-small;"&gt;NSIndexPath&lt;/span&gt; object that specifies a row in a specific section, or an &lt;span style="font-family: &amp;quot;;"&gt;NSInteger&lt;/span&gt; that specifies a section.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;The most obvious way to implement this would be through a &lt;span style="font-family: &amp;quot;;"&gt;switch&lt;/span&gt; statement in each of these methods. In the case of my program, this rapidly became unworkable, since I have 5 different sections, some with their own custom view types and specific logic for each section that uses a different portion of the data model and logic to generate the table view.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;The basis of my technique is to create a main view controller for the table view, in this case called &lt;span style="font-family: &amp;quot;; font-size: x-small;"&gt;NoteViewController&lt;/span&gt;, and separate section controllers for each section that inherit from an &lt;span style="font-family: &amp;quot;; font-size: x-small;"&gt;AbstractNoteSectionController&lt;/span&gt; class that I created. This class defines the interface for the section controllers and provides reasonable default behavior in case I don't need specific behavior in my subclasses. The interface for this class is:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;@interface AbstractNoteSectionController: NSObject {&lt;br /&gt;NSUInteger section;&lt;br /&gt;NoteViewController *parent;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property NSUInteger section;&lt;br /&gt;@property (nonatomic,retain) NoteViewController *parent;&lt;br /&gt;&lt;br /&gt;- (id)initWithNoteViewController:(NoteViewController *)cnt&lt;br /&gt;                      andSection:(NSUInteger)aSection;&lt;br /&gt;&lt;br /&gt;- (NSUInteger)numberOfRows;&lt;br /&gt;&lt;br /&gt;- (UITableViewCell *)cellForRow:(NSUInteger)row;&lt;br /&gt;&lt;br /&gt;- (UITableViewCellEditingStyle)editingStyleForRow:&lt;br /&gt;   (NSUInteger)row;&lt;br /&gt;&lt;br /&gt;- (void)selectedRow:(NSUInteger)row&lt;br /&gt;    withEditingStyle:(UITableViewCellEditingStyle)style;&lt;br /&gt;&lt;br /&gt;- (void)commitEditingStyle:(UITableViewCellEditingStyle)style&lt;br /&gt;                    forRow:(NSUInteger)row;&lt;br /&gt;&lt;br /&gt;- (BOOL)canEditRow:(NSUInteger)row;&lt;br /&gt;&lt;br /&gt;- (BOOL)shouldIndentWhileEditingRow:(NSUInteger)row;&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;I've only implemented the table view delegate methods that I need for my application. I then subclass this  and implement the specific functionality for each section in a separate subclass. In the main &lt;span style="font-family: &amp;quot;; font-size: x-small;"&gt;NoteViewController&lt;/span&gt; class, I have a &lt;span style="font-family: &amp;quot;; font-size: x-small;"&gt;sectionDict&lt;/span&gt; instance variable that has an &lt;span style="font-family: &amp;quot;; font-size: x-small;"&gt;NSDictionary&lt;/span&gt; object keyed by the section number:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre&gt;// Build a dispatch table so we can more&lt;br /&gt;// easily navigate all the sections.&lt;br /&gt;- (NSMutableDictionary *)buildSectionDict {&lt;br /&gt;  return [NSMutableDictionary dictionaryWithObjectsAndKeys:&lt;br /&gt;    [[[FirstSectionController alloc]&lt;br /&gt;      initWithNoteViewController:self&lt;br /&gt;                      andSection:FIRST_SECTION] autorelease], &lt;br /&gt;    [NSNumber numberWithUnsignedInteger:FIRST_SECTION],&lt;br /&gt;...&lt;br /&gt;    [[[LastSectionController alloc]&lt;br /&gt;      initWithNoteViewController:self&lt;br /&gt;                      andSection:LAST_SECTION] autorelease],&lt;br /&gt;    [NSNumber numberWithUnsignedInteger:LAST_SECTION], nil];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;Then, I have a method to select the section controller given a section:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;- (AbstractNoteSectionController *)&lt;br /&gt;    sectionControllerForSection:(NSUInteger)aSection {&lt;br /&gt;  NSNumber *section =&lt;br /&gt;    [NSNumber numberWithUnsignedInteger:aSection];&lt;br /&gt;  return (AbstractNoteSectionController *)&lt;br /&gt;    [sectionDict objectForKey:section];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;After this, something like &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;tableView:numberOfRowsInSection:&lt;/span&gt; is reduced to this simple piece of code from a fairly complex and difficult to maintain switch statement:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSInteger)tableView:(UITableView *)tableView&lt;br /&gt;  numberOfRowsInSection:(NSInteger)section {&lt;br /&gt;  return [[self sectionControllerForSection:section]&lt;br /&gt;    numberOfRows];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;This allows each section controller to be focused and readable. For example, the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;numberOfRows&lt;/span&gt; method for the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;PhotosSectionController&lt;/span&gt; is very simple.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;- (NSUInteger)numberOfRows {&lt;br /&gt;  return ((self.cameraAvailable) ? 2 : 1) +&lt;br /&gt;      [parent.note countOfPhotos];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;I suppose that I should mention at this point that each section controller has a parent member that &lt;/span&gt;points to the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;NoteViewController&lt;/span&gt; which still mediates access to the model (&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;parent.note&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;&lt;span style="font-family: &amp;quot;;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;I love programming with this type of dispatch table. It makes for extremely flexible code that is not at all brittle, because I can add or remove sections from the table view by simply editing the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;buildSectionDict&lt;/span&gt; method and writing a new subclass of &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;AbstractNoteSectionController&lt;/span&gt;.&amp;nbsp; No other methods of the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;NoteViewController&lt;/span&gt; ever have to change, which is great because it gives me fewer things to think about.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;Using this technique, I was able to split up a hard to read and manage 700 line behemoth class into much more managable 80-200 line pieces and improve the readability and flexibilty of my code immensely. Hope you find this useful in your iPhone development.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-100394964413353683?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/100394964413353683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=100394964413353683' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/100394964413353683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/100394964413353683'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2008/10/organizing-complex-uitableview-with.html' title='Organizing a complex UITableView with the iPhone SDK by using a dispatch table'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-166098229388161029</id><published>2008-07-30T10:19:00.001-07:00</published><updated>2008-07-30T10:28:49.612-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone sdk'/><category scheme='http://www.blogger.com/atom/ns#' term='nda'/><title type='text'>Frustration over iPhone SDK NDA</title><content type='html'>Just a short post. I wanted to express my frustration over the situation with the iPhone SDK and the NDA. I've been itching to start posting about iPhone development for a while now, and figured that I'd be able to do so as soon as iPhone OS 2.0 shipped. Here's hoping that Apple will make the decision to lift it soon. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some links about this topic:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://weblog.infoworld.com/enterprisemac/archives/2008/07/the_seal_on_app.html"&gt;The seal on Apple's iPhone SDK is leaking, even within Apple, Tom Yeager, Infoworld&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://arstechnica.com/news.ars/post/20080728-iphone-nda-doing-more-harm-than-good.html"&gt;IPhone NDA: Doing more harm than good, Chris Foresman, ars technica&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;These are just two of many. Come on, Apple allow us to blog, write books and discuss the SDK. The community will grow and you'll get better apps.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-166098229388161029?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/166098229388161029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=166098229388161029' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/166098229388161029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/166098229388161029'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2008/07/frustration-over-iphone-sdk-nda.html' title='Frustration over iPhone SDK NDA'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-6499814007483689735</id><published>2008-03-28T17:15:00.009-07:00</published><updated>2008-03-31T19:29:53.990-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QCView'/><category scheme='http://www.blogger.com/atom/ns#' term='quartz composer'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Possible Interface Builder bug with QCView in Leopard</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/R-3eXSBueRI/AAAAAAAAAG8/zzUy6_A6H6c/s1600-h/Interface+BuilderScreenSnapz001.jpg"&gt;&lt;/a&gt;&lt;br /&gt;A friend of mine was trying to embed a Quartz Composer composition into a Cocoa app, and he was having problems getting it to work properly. The composition responded to mouse events and it worked just fine within QC, but for some reason not within a QCView. I took a look at the project and he had done everything right, as far as I could tell; the "ForwardAllEvents" checkbox was checked. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://bp2.blogger.com/_QGaeGVxB4Zg/R-3eXSBueRI/AAAAAAAAAG8/zzUy6_A6H6c/s400/Interface+BuilderScreenSnapz001.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5183043237730744594" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; " /&gt;&lt;/div&gt;&lt;div&gt;So, I decided to perform a little experiment. I added a view controller to the application with the QCView as an outlet.&lt;br /&gt;&lt;br /&gt;Lo and behold, the eventForwardingMask was set to 1, not NSAnyEventMask as it should have been. The solution then presented itself: Set this value correctly in the -awakeFromNib and now the application worked properly&lt;/div&gt;&lt;div&gt;Here's the header file:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;#import &amp;lt;cocoa/cocoa.h&amp;gt;&lt;br /&gt;#import &amp;lt;quartzcomposer/qcview.h&amp;gt;&lt;br /&gt;&lt;br /&gt;@interface MyViewController : NSObject {&lt;br /&gt;  IBOutlet QCView *myView;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;And here's the implementation:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;#import "MyViewController.h"&lt;br /&gt;&lt;br /&gt;@implementation MyViewController&lt;br /&gt;&lt;br /&gt;- (void)awakeFromNib&lt;br /&gt;{&lt;br /&gt;  NSLog(@"%d", [myView eventForwardingMask]);&lt;br /&gt;  [myView setEventForwardingMask:NSAnyEventMask];&lt;br /&gt;  NSLog(@"%d", [myView eventForwardingMask]);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;I'm going to file a bugreporter bug right away.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-6499814007483689735?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/6499814007483689735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=6499814007483689735' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/6499814007483689735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/6499814007483689735'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2008/03/possible-interface-builder-bug-with.html' title='Possible Interface Builder bug with QCView in Leopard'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_QGaeGVxB4Zg/R-3eXSBueRI/AAAAAAAAAG8/zzUy6_A6H6c/s72-c/Interface+BuilderScreenSnapz001.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-9007193391697809397</id><published>2007-07-23T12:05:00.000-07:00</published><updated>2007-07-23T12:17:09.128-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='transition'/><category scheme='http://www.blogger.com/atom/ns#' term='linkedin'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Breaking news.</title><content type='html'>Greetings to all. Wow, it's been quite a while since I've blogged and much has happened  since then. I still plan on updating this with some more AppleScript stuff and some other things I've been getting interested in, but I've just been too busy. Stay tuned for more.&lt;br /&gt;&lt;br /&gt;The most important news is that I've accepted a position at Google. Pretty exciting for me to be moving from my remote village here in Baltimore to the heart of the Silicon Valley. How this all happened is that I got an email from a Google recruiter through my profile on &lt;a href="http://www.linkedin.com/in/paulfranceus"&gt;linkedin.com&lt;/a&gt; (I highly recommend this site to everyone), and figured, sure, what the heck, I'll talk to you. After two phone interviews I got a rejection email while I was at WWDC. A week later, I got a call from another recruiter saying that they wanted to bring me in for a live interview. Flew out and had a day of discussion and answering lots of questions and here I sit, offer in hand. I'm going to start there in the end of September. I figure that I've been with my current company for a long long time and it's time for something different. East coast to west coast, Government contractor to commercial search engine giant. Should be fun.&lt;br /&gt;&lt;br /&gt;Anyway, it's been a bit of a busy couple of months.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-9007193391697809397?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/9007193391697809397/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=9007193391697809397' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/9007193391697809397'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/9007193391697809397'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/07/breaking-news.html' title='Breaking news.'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-1581762897020260325</id><published>2007-06-06T19:31:00.000-07:00</published><updated>2007-06-06T19:34:58.688-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WWDC'/><title type='text'>Heading to WWDC.</title><content type='html'>Well, I'm very excited this year to be off to WWDC again. Blogging this year has been a pleasure and I hope that I can meet some of the people that have made this a worthwhile exercise for me.&lt;br /&gt;&lt;br /&gt;Thanks,&lt;br /&gt;&lt;br /&gt;Paul Franceus&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-1581762897020260325?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/1581762897020260325/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=1581762897020260325' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1581762897020260325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1581762897020260325'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/06/heading-to-wwdc.html' title='Heading to WWDC.'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-5897001905857117457</id><published>2007-06-03T15:54:00.000-07:00</published><updated>2007-06-03T17:10:29.449-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='script editor'/><category scheme='http://www.blogger.com/atom/ns#' term='DEVONagent'/><category scheme='http://www.blogger.com/atom/ns#' term='applescript'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa bindings'/><category scheme='http://www.blogger.com/atom/ns#' term='automator'/><title type='text'>Automator - Cocoa based action with AppleScript integration</title><content type='html'>&lt;span style="font-family:georgia;"&gt;I've built a bunch of &lt;a href="http://www.apple.com/macosx/features/automator/"&gt;Automator&lt;/a&gt; actions lately. Most of these have been simple AppleScript actions, with a few Shell based actions thrown in. I'll probably talk about some of those in future postings.&lt;br /&gt;&lt;br /&gt;Right now I want to talk about one particular action I built that incorporated a combination of &lt;a href="http://www.apple.com/macosx/features/applescript/"&gt;AppleScript&lt;/a&gt; and Objective-C code. There are a couple of ways to build such a &lt;a href="http://developer.apple.com/documentation/AppleApplications/Conceptual/AutomatorConcepts/Articles/ImplementScriptAction.html#//apple_ref/doc/uid/TP40001512-96897-CJBEJFAF"&gt;“hybrid” action&lt;/a&gt; as Apple's documentation calls it. One way is to use primarily AppleScript, with added Objective-C classes that are called from the AppleScript. The other way is to &lt;a href="http://developer.apple.com/documentation/AppleApplications/Conceptual/AutomatorConcepts/Articles/ImplementObjCAction.html#//apple_ref/doc/uid/TP40001513-BCICCECC"&gt;primarily use Objective-C,&lt;/a&gt; with small amounts of AppleScript as necessary.&lt;br /&gt;&lt;br /&gt;The reason I went down this road is that I was building a search action for &lt;a href="http://www.devon-technologies.com/products/devonagent/"&gt;DEVONagent&lt;/a&gt;. DEVONagent is a very cool program that allows you to search using multiple search engines, following links along the way, and aggregating the results. It then extracts the common key topics from the web pages it searches and builds clustering diagrams based on the results. Here's an example based on a search for “Apple WWDC”&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RmNIneZvriI/AAAAAAAAADk/859o3Uc3PbI/s1600-h/wwdc+search+results.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RmNIneZvriI/AAAAAAAAADk/859o3Uc3PbI/s400/wwdc+search+results.png" alt="" id="BLOGGER_PHOTO_ID_5071977448362454562" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;DEVONagent showing search results&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;DEVONagent comes with some useful actions out of the box, but it does not come with an action to initiate a search(!), so I decided to write one. One of the main features of the program is its use of “search sets” and plugins to allow searches to be customized. That search set list can be dynamic - users can develop their own custom search sets for specialized sites or for any reason whatsoever.&lt;br /&gt;&lt;br /&gt;Therefore I wanted my UI for my action to reflect the current list of search sets available in the program. So, I needed to be able to query DEVONagent for its list of search sets, and then populate a popup menu in the action's interface with the current list. Then, I wanted the selected search set to be used to perform the search.&lt;br /&gt;&lt;br /&gt;To me, querying the list of search sets, and executing the search sound like jobs for AppleScript, and populating a user interface popup menu sounds like a job for Cocoa bindings and Objective-C. I believe that this type of action is possible to write completely in AppleScript, but at this point my Obj-C is much stronger than my Applescript so I decided to call AppleScript from my Cocoa based action.&lt;br /&gt;&lt;br /&gt;The first step for me was to take a look at DEVONagent's scripting dictionary to see if it could do what I wanted it to do. The program has a pretty extensive scripting dictionary and I quickly found what I needed using the &lt;a href="http://www.apple.com/applescript/scripteditor/"&gt;Script Editor&lt;/a&gt; application. Here's the application dictionary showing search sets:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RmNJJeZvrjI/AAAAAAAAADs/QAFQBXbOOq0/s1600-h/devonagent+app+dictionary.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RmNJJeZvrjI/AAAAAAAAADs/QAFQBXbOOq0/s400/devonagent+app+dictionary.png" alt="" id="BLOGGER_PHOTO_ID_5071978032478006834" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;DEVONagent "application" object dictionary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;And here's the small script I wrote to query the app for the list of search sets:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RmNJweZvrkI/AAAAAAAAAD0/RKPru4aAtXo/s1600-h/listing+search+sets.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RmNJweZvrkI/AAAAAAAAAD0/RKPru4aAtXo/s400/listing+search+sets.png" alt="" id="BLOGGER_PHOTO_ID_5071978702492905026" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;AppleScript to list search sets&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;Executing a search was similarly easy. Here's the search command in the dictionary:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RmNMAuZvrmI/AAAAAAAAAEE/J7ikOaSotzA/s1600-h/script+editor+search+action.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RmNMAuZvrmI/AAAAAAAAAEE/J7ikOaSotzA/s400/script+editor+search+action.png" alt="" id="BLOGGER_PHOTO_ID_5071981180689034850" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;And here's the script to run a search. I chose to search for “Apple WWDC” for which I've already shown the results.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNL6-ZvrlI/AAAAAAAAAD8/hM1jKABoppQ/s1600-h/devonagent+search+command.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNL6-ZvrlI/AAAAAAAAAD8/hM1jKABoppQ/s400/devonagent+search+command.png" alt="" id="BLOGGER_PHOTO_ID_5071981081904787026" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Now, I have to build the action. Starting XCode, I'll choose “Cocoa Automator action” from the project list and call it “Perform Search With Specified Text” The name is long because the project name is what shows up in Automator and it needs to be descriptive. This action is intended to use text fed in as input from the previous action as the search terms and search using whatever search set the user selects.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNMS-ZvrnI/AAAAAAAAAEM/GywXpDAvJnU/s1600-h/choose+a+project.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNMS-ZvrnI/AAAAAAAAAEM/GywXpDAvJnU/s400/choose+a+project.png" alt="" id="BLOGGER_PHOTO_ID_5071981494221647474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The project defines a class for the action that is a subclass of &lt;a href="http://developer.apple.com/documentation/AppleApplications/Reference/AutomatorFramework/Classes/AMBundleAction_Class/Reference/Reference.html"&gt;&lt;span style="font-family:courier new;"&gt;AMBundleAction&lt;/span&gt;&lt;/a&gt;. There is also a nib file, which presents the interface for the action. Let's start with our Objective-C code. The class predefines a method called &lt;span style="font-family:courier new;"&gt;-(id)runWithInput:(id) fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo&lt;/span&gt;. We've added one getter method to return the list of search sets so we can bind the popup in the GUI to something. Here's the header for the class:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;br /&gt;#import &amp;lt;Automator/AMBundleAction.h&amp;gt;&lt;br /&gt;&lt;br /&gt;@interface Perform_Search_with_Specified_Text : AMBundleAction &lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;- (NSArray *)searchSets&lt;br /&gt;- (id)runWithInput:(id)input &lt;br /&gt;        fromAction:(AMAction *)anAction &lt;br /&gt;             error:(NSDictionary **)errorInfo;&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family: georgia;"&gt;Here's the implementation:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#import “Perform Search with Specified Text.h“&lt;br /&gt;#import “DevonAgent.h“&lt;br /&gt;&lt;br /&gt;@implementation Perform_Search_with_Specified_Text&lt;br /&gt;&lt;br /&gt;static DevonAgent *devonAgent = nil;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family: georgia;"&gt;I've created a DevonAgent class to encapsulate all interaction with the application. I suspect that at some time in the future the need to write this class will go away.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;- (void)awakeFromBundle{&lt;br /&gt;    if( devonAgent == nil ){&lt;br /&gt;        devonAgent = [[DevonAgent alloc] init];&lt;br /&gt;    }&lt;br /&gt;    [[self parameters] setValue:[devonAgent defaultSearchSet] &lt;br /&gt;                         forKey:@“selectedSearchSet“];&lt;br /&gt;    [self parametersUpdated];&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;-awakeFromBundle&lt;/span&gt; is called when this class is loaded, similarly to how &lt;span style="font-family:courier new;"&gt;-awakeFromNib&lt;/span&gt; is called when a class is instantiated out of the nib. This is where we connect to the DevonAgent class.  We also set the initial &lt;span style="font-family:courier new;"&gt;selectedSearchSet&lt;/span&gt; parameter so that the user interface will reflect the selection. You call &lt;span style="font-family:courier new;"&gt;-parametersUpdated&lt;/span&gt; to tell the user interface to update its configuration based on the parameters.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;- (NSArray *)searchSets{&lt;br /&gt;    return [devonAgent searchSets];&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When the interface needs the search sets, we go ask for it from the app.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;- (id)runWithInput:(id)input &lt;br /&gt;        fromAction:(AMAction *)anAction &lt;br /&gt;             error:(NSDictionary **)errorInfo&lt;br /&gt;{&lt;br /&gt;    NSString *selectedSearchSet = &lt;br /&gt;  [[self parameters] valueForKey:@“selectedSearchSet“];&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;At this point we grab our parameters and get the “selectedSearchSet” parameter. I'll talk about how the parameters get set in a little while. Suffice it to say at this point that there are parameters and that these are set by selecting controls in the GUI and they are populated by Cocoa Bindings in the nib file.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;    [devonAgent performSearchFor:input &lt;br /&gt;                  usingSearchSet:selectedSearchSet &lt;br /&gt;                           error:errorInfo];&lt;br /&gt;    return input;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now, &lt;span style="font-family:courier new;"&gt;runWithInput:fromAction:error&lt;/span&gt; is called when the action is run. We simply grab the currently selected search set out of the parameters for this action and launch the search. We're not returning the results of the search in this action. When DEVONagent runs, we can tell it to run a script when the action finishes (it can take quite a while for these searches to run, depending on the settings) and at that point we'd want to launch the next phase of the workflow.&lt;br /&gt;&lt;br /&gt;This part of the action is pretty straighforward. Here's the DevonAgent class interface:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;br /&gt;&lt;br /&gt;@interface DevonAgent : NSObject {&lt;br /&gt;}&lt;br /&gt;- (NSString *)defaultSearchSet;&lt;br /&gt;- (NSMutableArray *)searchSets;&lt;br /&gt;- (void)performSearchFor:(NSString *)search &lt;br /&gt;          usingSearchSet:(NSString *)searchSet &lt;br /&gt;                   error:(NSDictionary **)errorInfo;&lt;br /&gt;@end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And the implementation:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@implementation DevonAgent&lt;br /&gt;&lt;br /&gt;- (NSString *)defaultSearchSet{&lt;br /&gt;    return @“Internet (Fast Scan)“;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This method is used to make sure that there is a selected search set when the interface is first populated. This is a good default.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;- (NSMutableArray *)searchSets{&lt;br /&gt;    NSMutableArray *searchSets = &lt;br /&gt;     [[[NSMutableArray alloc] init] autorelease];&lt;br /&gt;    NSString *script = &lt;br /&gt;     @“tell application \“DEVONagent\“ to return search sets“;&lt;br /&gt;    NSAppleScript *getSearchSets = &lt;br /&gt;     [[[NSAppleScript alloc] initWithSource:script] &lt;br /&gt;         autorelease];&lt;br /&gt;    NSDictionary *error;&lt;br /&gt;    NSAppleEventDescriptor *result = &lt;br /&gt;     [getSearchSets executeAndReturnError:&amp;error];&lt;br /&gt;    int i;&lt;br /&gt;    int items = [result numberOfItems];&lt;br /&gt;    //Descriptor list index is one based!&lt;br /&gt;    for( i = 1; i &lt;= items; i++ ){&lt;br /&gt;        NSString *setName = &lt;br /&gt;          [[result descriptorAtIndex:i] stringValue];&lt;br /&gt;        [searchSets addObject:setName];&lt;br /&gt;    }&lt;br /&gt;    return searchSets;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The searchSets method executes the AppleScript code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;tell application 'DEVONagent' to return search sets&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is equivalent to but shorter than:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;tell application 'DEVONagent'&lt;br /&gt; return search sets&lt;br /&gt;end tell&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The result of executing an AppleScript program is an &lt;a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSAppleEventDescriptor_Class/Reference/Reference.html"&gt;&lt;span style="font-family:courier new;"&gt;NSEventDescriptor&lt;/span&gt;&lt;/a&gt; object which contains the results of the script. In this case, it contains the list of names of all the current search sets. Because this will get called as soon as the action is dragged out into the workflow, you shouldn't be surprised to see DEVONagent launch itself at that time. One thing that is important to remember is that the index in a NSAppleEventDescriptor list starts at one, not zero as you might expect.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;- (void)performSearchFor:(NSString *)search &lt;br /&gt;          usingSearchSet:(NSString *)searchSet &lt;br /&gt;                   error:(NSDictionary **)errorInfo{&lt;br /&gt;    NSString *script;&lt;br /&gt;    NSString *searchTemplate = &lt;br /&gt;     @“tell application \“DEVONagent\“ to search \“%@\“\&lt;br /&gt;       using set \“%@\““;&lt;br /&gt;    script = [NSString stringWithFormat:searchTemplate, &lt;br /&gt;              search, searchSet];&lt;br /&gt;    NSAppleScript *as = &lt;br /&gt;      [[NSAppleScript alloc] initWithSource:script];&lt;br /&gt;    [as executeAndReturnError:errorInfo];&lt;br /&gt;    [as release];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This method executes the search.&lt;br /&gt;&lt;br /&gt;On to the nib.  When you open the &lt;span style="font-family:courier new;"&gt;MainMenu.nib&lt;/span&gt; file for your action you will see a view with nothing but a placeholder string. The nib file also contains a controller object called “&lt;span style="font-family:courier new;"&gt;Parameters&lt;/span&gt;.” This controller is used to pass parameters from the interface to the script. In the case of the Cocoa action, the &lt;span style="font-family:courier new;"&gt;AMBundleAction&lt;/span&gt; class has a &lt;span style="font-family:courier new;"&gt;-parameters&lt;/span&gt; method that returns an &lt;span style="font-family:courier new;"&gt;NSMutableDictionary&lt;/span&gt; containing the parameters defined in the nib.&lt;br /&gt;&lt;/cocoa&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RmNQcuZvroI/AAAAAAAAAEU/Kqs6PoUF-vc/s1600-h/Nib+file.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RmNQcuZvroI/AAAAAAAAAEU/Kqs6PoUF-vc/s400/Nib+file.png" alt="" id="BLOGGER_PHOTO_ID_5071986059771883138" border="0" /&gt;&lt;/a&gt;&lt;br /&gt; The first thing I'll do here is to build my view. It's pretty simple, a string and an &lt;span style="font-family:courier new;"&gt;NSPopUpButton&lt;/span&gt;.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/RmNQnOZvrpI/AAAAAAAAAEc/Wg8p841kNu0/s1600-h/action%27s+view.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/RmNQnOZvrpI/AAAAAAAAAEc/Wg8p841kNu0/s400/action%27s+view.png" alt="" id="BLOGGER_PHOTO_ID_5071986240160509586" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;We will bind the &lt;span style="font-family:courier new;"&gt;"contentValues"&lt;/span&gt; of the &lt;span style="font-family:courier new;"&gt;NSPopUpButton&lt;/span&gt; to the “&lt;span style="font-family:courier new;"&gt;searchSets&lt;/span&gt;” key of the file's owner, which is our action class. This will make sure that the list reflects the search sets in the application. We bind the &lt;span style="font-family:courier new;"&gt;“selectedValue”&lt;/span&gt; to the &lt;span style="font-family:courier new;"&gt;Parameters&lt;/span&gt; object controller &lt;span style="font-family:courier new;"&gt;selection.selectedSearchSet&lt;/span&gt; to make sure that the parameters get the updated value.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNRT-ZvrqI/AAAAAAAAAEk/pcgZrLATcwY/s1600-h/NSPopUpButton+bindings.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNRT-ZvrqI/AAAAAAAAAEk/pcgZrLATcwY/s400/NSPopUpButton+bindings.png" alt="" id="BLOGGER_PHOTO_ID_5071987008959655586" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;That's all that's necessary for the nib file.&lt;br /&gt;&lt;br /&gt;There's only two more steps. First, we have to edit the &lt;span style="font-family:courier new;"&gt;info.plist&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;infoPlist.strings&lt;/span&gt; to define our inputs and outputs and to fill in the description window in Automator. To do that, we will select &lt;span style="font-family:courier new;"&gt;Project &gt; Edit Active Target “Perform Search with Specified Text”&lt;/span&gt; and select the &lt;span style="font-family:courier new;"&gt;Properties&lt;/span&gt; tab. Here are my settings for each of the panes of this dialog box that I modified:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNRz-ZvrsI/AAAAAAAAAE0/zIsnPQ9umhA/s1600-h/info+plist+parameters+tab.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNRz-ZvrsI/AAAAAAAAAE0/zIsnPQ9umhA/s400/info+plist+parameters+tab.png" alt="" id="BLOGGER_PHOTO_ID_5071987558715469506" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RmNRuuZvrrI/AAAAAAAAAEs/2sKRXXNDkZY/s1600-h/info+plist+general+tab.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RmNRuuZvrrI/AAAAAAAAAEs/2sKRXXNDkZY/s400/info+plist+general+tab.png" alt="" id="BLOGGER_PHOTO_ID_5071987468521156274" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;cocoa&gt;I don't edit the description here in this dialog box since the localized ones in infoPlist.strings seem to override them. Here's what the action looks like in Automator once all the documentation has been filled in.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNSA-ZvrtI/AAAAAAAAAE8/hNWttON4mAU/s1600-h/automator+with+my+action.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RmNSA-ZvrtI/AAAAAAAAAE8/hNWttON4mAU/s400/automator+with+my+action.png" alt="" id="BLOGGER_PHOTO_ID_5071987782053768914" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The last step is testing. One of the great things about developing automator actions is that they are very easy to debug. Whether you are developing in Objective-C or Cocoa, the XCode debugger will launch automator for you, load your action into the program, and then when you run your workflow you can debug the action just like any other program.&lt;br /&gt;&lt;br /&gt;Well, that's how I built a Cocoa based Automator action that uses AppleScript to communicate with one specific commercial app. The action uses AppleScript to extract information from a program and to control its operations. Cocoa and Cocoa bindings are used to populate a popup list with dynamic information from the app. I hope that you found this interesting and informative. Until next time, happy coding.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-5897001905857117457?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/5897001905857117457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=5897001905857117457' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/5897001905857117457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/5897001905857117457'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/06/automator-cocoa-based-action-with.html' title='Automator - Cocoa based action with AppleScript integration'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_QGaeGVxB4Zg/RmNIneZvriI/AAAAAAAAADk/859o3Uc3PbI/s72-c/wwdc+search+results.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-4840796114016345699</id><published>2007-05-17T17:09:00.000-07:00</published><updated>2007-05-17T17:37:33.506-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='applescript'/><category scheme='http://www.blogger.com/atom/ns#' term='workflow'/><category scheme='http://www.blogger.com/atom/ns#' term='gui scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='automator'/><title type='text'>Heading down the road with Automator and AppleScript</title><content type='html'>Lately, I've been working with a large customer to prototype the use of Macs to see if they are a viable alternative to the Windows based platform that they have standardized on in the past. You and I know this to be the case, but they need some convincing. One of the most compelling things for the users at this customer appears to be the use of &lt;a href="http://www.apple.com/macosx/features/automator/"&gt;Automator&lt;/a&gt; to automate their individual workflows.&lt;br /&gt;&lt;br /&gt;First of all, the users I am dealing with are not programmers, but somehow when they see Automator they immediately get how it could help them get their jobs done. The problem I've had is that a lot of what these folks do involves querying multiple web sites and passing data from the results of one query to the next.&lt;br /&gt;&lt;br /&gt;Certainly, any specific workflow could be implemented in code by a developer, but what I really want to do is to figure out a way to provide them with the tools they need to be able to build their own custom workflows.&lt;br /&gt;&lt;br /&gt;So, this has gotten me to finally get off my tail and learn &lt;a href="http://www.apple.com/macosx/features/applescript/"&gt;AppleScript&lt;/a&gt; and Automator. As a developer, it seemed to me that Automator was nothing more than a flashy toy that would be difficult to get any real work done. But perhaps the truth was that I, with years of C, C++, Java, JavaScript, Objective-C, Perl and Python and my programmer's brain, am not the real target audience for this tool. And AppleScript has always seemed to be a very weird language with strange syntax and poor documentation. Heck some of the best programmers I know have stayed away.&lt;br /&gt;&lt;br /&gt;Well, necessity is a good teacher, and so lately I have been developing a lot of &lt;a href="http://www.apple.com/downloads/macosx/automator/"&gt;Automator actions&lt;/a&gt; to fill in the gaps I've seen in the built-in actions and third party additions. I've also become interested in how I can use &lt;a href="http://www.apple.com/applescript/uiscripting/"&gt;GUI scripting&lt;/a&gt; to enable users to more easily help themselves.&lt;br /&gt;&lt;br /&gt;Over the next several posts, I'll share some of my actions, talk about finally getting my head around AppleScript, and head down the GUI scripting road. Perhaps some of you will share some of your own experiences too. It would be great to hear from you.&lt;br /&gt;&lt;br /&gt;Here are some good links I've found regarding Automator and AppleScript:&lt;br /&gt;&lt;br /&gt;Automator:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.apple.com/macosx/automator.html"&gt;http://developer.apple.com/macosx/automator.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://automator.us/"&gt;http://automator.us&lt;/a&gt;&lt;br /&gt;&lt;a href="http://automatorworld.com/"&gt;http://automatorworld.com&lt;/a&gt;&lt;br /&gt;&lt;a href="http://automatoractions.com/"&gt;http://automatoractions.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;AppleScript:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://applescriptsourcebook.com/"&gt;http://applescriptsourcebook.com/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://developer.apple.com/applescript/"&gt;http://developer.apple.com/applescript/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://macscripter.net/"&gt;http://macscripter.net/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/gp/product/0596102119?ie=UTF8&amp;tag=fromaremotevi-20&amp;amp;linkCode=as2&amp;camp=1789&amp;amp;creative=9325&amp;creativeASIN=0596102119"&gt;AppleScript: The Definitive Guide, 2nd Edition&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fromaremotevi-20&amp;amp;l=as2&amp;o=1&amp;amp;a=0596102119" alt="" style="border: medium none  ! important; margin: 0px ! important;" border="0" height="1" width="1" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-4840796114016345699?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/4840796114016345699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=4840796114016345699' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/4840796114016345699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/4840796114016345699'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/05/heading-down-road-with-automator-and.html' title='Heading down the road with Automator and AppleScript'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-1281790590762700669</id><published>2007-05-12T12:46:00.000-07:00</published><updated>2007-05-12T13:00:52.477-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='commuting'/><category scheme='http://www.blogger.com/atom/ns#' term='cycling'/><title type='text'>Bicycle commuting around Baltimore</title><content type='html'>I'm a pretty avid cyclist, and I enjoy commuting to work on my bike. For the past couple of years,  I’ve been attempting to compile a list of routes that I use to get around the Baltimore area on my bike. I’d rather avoid driving my bike somewhere to ride, so I’m always looking for ways to jump on my bike at my back door. It’s a little more difficult to find good routes for a city dweller like myself, so I'll be posting them here from time to time in case anyone is interested.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.gmap-pedometer.com/?r=176566"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RkYbiH0JNLI/AAAAAAAAADc/EJ3Tcnn8DhE/s400/droppedImage.png" alt="" id="BLOGGER_PHOTO_ID_5063765104051958962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The first 18 miles of this &lt;a href="http://www.gmap-pedometer.com/?r=176566"&gt;route&lt;/a&gt; represent my standard commuting route for the past couple of years. It starts out on the &lt;a href="http://www.gwynnsfallstrail.org/"&gt;Gwynns Falls Trail&lt;/a&gt;, heads out Frederick Rd towards Catonsville, Once through Catonsville, I head south on Hilltop Rd, across the Patapsco river and up Ilchester Rd towards Columbia. The big challenge of this ride comes around mile 11. Heading out, you have to climb an 18 percent grade up the first part of Ilchester. The way home has a similar climb up Hilltop. A more scenic and easier climb up Bonnie Branch Rd. is also an alternative. Normally I’ll ride home the same way, but on weekends I often take the loop route home. The only major difficulty is dealing with the I 95 interchange off of 175, which is much lighter in traffic on the weekend. Mileage to work is about 18, total milage is around 41.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-1281790590762700669?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/1281790590762700669/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=1281790590762700669' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1281790590762700669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/1281790590762700669'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/05/bicycle-commuting-around-baltimore.html' title='Bicycle commuting around Baltimore'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_QGaeGVxB4Zg/RkYbiH0JNLI/AAAAAAAAADc/EJ3Tcnn8DhE/s72-c/droppedImage.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-8803792089164603951</id><published>2007-04-29T11:39:00.000-07:00</published><updated>2009-06-17T17:13:09.679-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QCView'/><category scheme='http://www.blogger.com/atom/ns#' term='convolution'/><category scheme='http://www.blogger.com/atom/ns#' term='quartz composer'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='bindings'/><title type='text'>Cocoa Application with custom Core Image filter 6: Embedding a Quartz Composer composition directly in an app.</title><content type='html'>In the last episode, we built a &lt;a href="http://fromaremotevillage.blogspot.com/2007/04/cocoa-application-with-custom-core.html"&gt;Cocoa application to filter an image using Convolution&lt;/a&gt;. This application followed a more traditional path to developing a Cocoa application. We took an image from our view, handed it to the model to be processed and sent it back to the view for display. With the advent of Quartz Composer, this application can be implemented in an entirely different way that opens up all kinds of interesting possibilities for interactive graphics applications. I'm sure all of you have seen that Quartz Compositions can respond to external inputs. Quartz Composer can take input from keyboard and mouse, spotlight image searches, folders of images, and even RSS feeds. Apple's RSS screensaver is a prime example of a Quartz Composition that uses RSS input.&lt;br /&gt;&lt;br /&gt;An other way of feeding input and getting output from a composition is by publishing input and output parameters from your compositions. In the most simple sense, these will allow Quartz Composer itself to present a simple UI to allow you to interact with your composition. For example, in my test.qtz composition, I publish the inputImage and all the coefficients so that QC can provide a simple user interface.&lt;br /&gt;&lt;br /&gt;More interesting than this, however, is the use of a composition embedded  within a QCView and accessing its parameters using Cocoa bindings technology and the QCPatchController. I've provided a link to this project so you can see how I did it: &lt;a href="http://sites.google.com/site/fromaremotevillage/Home/Convolver.zip?attredirects=0"&gt;Convolver.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The first thing you need to do to use these in your projects is to add the Quartz Composer palette to Interface Builder.  To do this:&lt;br /&gt;&lt;br /&gt;Open Interface Builder Preferences:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RjTnOH0JNDI/AAAAAAAAACc/BC-da0Z9qZo/s1600-h/IB+Prefs.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RjTnOH0JNDI/AAAAAAAAACc/BC-da0Z9qZo/s400/IB+Prefs.png" alt="" id="BLOGGER_PHOTO_ID_5058922511245849650" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:78%;"&gt;IB preferences showing palettes tab&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;Click the Add... button:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RjTn9H0JNEI/AAAAAAAAACk/5SEH7-KQ2As/s1600-h/Select+A+Palette.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RjTn9H0JNEI/AAAAAAAAACk/5SEH7-KQ2As/s400/Select+A+Palette.png" alt="" id="BLOGGER_PHOTO_ID_5058923318699701314" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:78%;"&gt;selecting the QuartzComposer palette&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;You will get this new palette in Interface Builder.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RjTovn0JNFI/AAAAAAAAACs/AilOu4xjk_A/s1600-h/QCPalette.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RjTovn0JNFI/AAAAAAAAACs/AilOu4xjk_A/s400/QCPalette.png" alt="" id="BLOGGER_PHOTO_ID_5058924186283095122" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-size:78%;"&gt;the Quartz Composer palette&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;Next, we need to copy the test.qtz composition into the bundle for our application. It doesn't really matter where you put it. For our purposes, we just need to make sure that it is packaged with the app when it ships. After that we need to open the composition and make sure that we publish all the inputs that we care about so that the QCPatchController can access them.  In order to do this, you have to right-click on whatever patches in you composition have external inputs or outputs  and check off each one you wish to publish. As far as I've been able to tell, there are no shortcuts to this, you have to select each one individually.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RjTp130JNGI/AAAAAAAAAC0/nykQpEFV-Mw/s1600-h/published+inputs.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RjTp130JNGI/AAAAAAAAAC0/nykQpEFV-Mw/s400/published+inputs.png" alt="" id="BLOGGER_PHOTO_ID_5058925393168905314" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;Publishing inputs from the compostion&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;Whatever you name the inputs or outputs at this point is the key that will be used to access that parameter later.&lt;br /&gt;&lt;br /&gt;Now that we have a properly set up composition, we have to modify the original project to use the QCView  and QCPatchController instead of old-school target/action. First thing to do is to replace the outputImage NSImageView on the right of the main window of the app with a QCView. Then, drag a QCPatchController into the nib file. I set the erase color of the QCView to transparent to avoid the large black square on the right side of the application when it starts.&lt;br /&gt;&lt;br /&gt;Next., we'll go ahead and set up the bindings for the project. The QCView's patch binding is bound to the patch key on the QCPatchController.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RjTqyH0JNHI/AAAAAAAAAC8/f6IMkTq6iQw/s1600-h/QCView+bindings.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RjTqyH0JNHI/AAAAAAAAAC8/f6IMkTq6iQw/s400/QCView+bindings.png" alt="" id="BLOGGER_PHOTO_ID_5058926428256023666" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;We set the value binding of the NSImageView to the patch of the QCPatchController with the src.value key path.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RjTwzn0JNII/AAAAAAAAADE/ANsNOwbHYUk/s1600-h/imageView+bindings.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RjTwzn0JNII/AAAAAAAAADE/ANsNOwbHYUk/s400/imageView+bindings.png" alt="" id="BLOGGER_PHOTO_ID_5058933051095594114" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Finally, each cell of the NSMatrix gets bound to the correct value in the composition:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/RjTxLX0JNJI/AAAAAAAAADM/azT1uubxTtQ/s1600-h/text+cell+bindings.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/RjTxLX0JNJI/AAAAAAAAADM/azT1uubxTtQ/s400/text+cell+bindings.png" alt="" id="BLOGGER_PHOTO_ID_5058933459117487250" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The final piece of this setup is to load the composition into the QCPatchController&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RjUH5n0JNKI/AAAAAAAAADU/vexInZe6PEY/s1600-h/Load+Composition.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RjUH5n0JNKI/AAAAAAAAADU/vexInZe6PEY/s400/Load+Composition.png" alt="" id="BLOGGER_PHOTO_ID_5058958442942248098" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Now, you can actually test the working application from within IB. If you choose File-&gt;Test Interface and drop an image on the left image view you will see the convolved output image displayed on the right. Of course, this is exploring the barest minimum of the possibly utility of Quartz Compositions and QCView.&lt;br /&gt;&lt;br /&gt;This has greatly reduced the amount of Objective-C code we need to write to build this application. The entire Convolver class that converted the image to a CIImage, called the filter and reconverted the result back into an NSImage is not needed any more. I still want to be able to select File-&gt;Open... to drop down a sheet and open the image, so that's really the only thing I need the ConvolutionController to do at this point. There turns out to be one small catch that I'll point out when I get to it.&lt;br /&gt;&lt;br /&gt;Here's the new header for the ConvolverController class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/* ConvolverController */&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;QuartzComposer/QuartzComposer.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@interface ConvolverController : NSObject&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet QCView *resultImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet NSImageView *sourceImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet NSWindow *window;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (IBAction)openImage:(id)sender;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;Some of you may look at this and wonder why I need a pointer to the resultImage any more since the bindings should take care of it. That's where the catch comes in, as you'll see below.&lt;br /&gt;&lt;br /&gt;This is the source code for the class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import “ConvolverController.h“&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@implementation ConvolverController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (IBAction)openImage:(id)sender&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSOpenPanel *panel = [NSOpenPanel openPanel];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [panel beginSheetForDirectory: nil&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                             file:nil&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                            types:&lt;br /&gt;               [NSImage imageFileTypes]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                   modalForWindow: window &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                    modalDelegate:self &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                   didEndSelector:&lt;br /&gt;  @selector(openPanelDidEnd:returnCode:contextInfo:)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                      contextInfo:nil];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (void)openPanelDidEnd:(NSOpenPanel *)panel&lt;br /&gt;           returnCode:(int)returnCode&lt;br /&gt;          contextInfo:(void  *)contextInfo{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSArray *files = [panel filenames];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSString *filename = [files objectAtIndex:0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSImage *image = [[[NSImage alloc]&lt;br /&gt;    initByReferencingFile:filename]&lt;br /&gt;       autorelease];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [sourceImage setImage: image];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [resultImage setValue: image forInputKey: @“src“];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is normal sheet handling code, just like in the previous project, but a little simpler. The catch comes in when I have to call &lt;span style="font-family:courier new;"&gt;[resultImage setValue: image forInputKey: @“src“]&lt;/span&gt; even though you might think that the bindings for NSImageView should automatically be updated, they are not. Apparently it's an already filed bug and this one line of code provides a simple workaround.&lt;br /&gt;&lt;br /&gt;So, that ends the journey of building a Cocoa app with a custom Core Image filter. Hope it was useful to you. This was a very simple example, there's so much more that can be done with these amazing technologies. Until next time, happy coding!&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-8803792089164603951?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/8803792089164603951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=8803792089164603951' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/8803792089164603951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/8803792089164603951'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/04/cocoa-application-with-custom-core_29.html' title='Cocoa Application with custom Core Image filter 6: Embedding a Quartz Composer composition directly in an app.'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_QGaeGVxB4Zg/RjTnOH0JNDI/AAAAAAAAACc/BC-da0Z9qZo/s72-c/IB+Prefs.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-2833196858975859639</id><published>2007-04-10T19:32:00.000-07:00</published><updated>2007-05-11T04:47:14.871-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='core image'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='cifilter'/><category scheme='http://www.blogger.com/atom/ns#' term='image unit'/><title type='text'>Cocoa application with custom Core Image filter 5: calling the filter from a Cocoa app.</title><content type='html'>Last time in the village &lt;a href="http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_29.html"&gt;we packaged our convolution filter as an image unit&lt;/a&gt;. Since the filter was executable, we had to develop an Objective-C class that inherited from &lt;span style="font-family:courier new;"&gt;CIFilter&lt;/span&gt; and implemented a few methods. This time we will see how we can call our image unit from a Cocoa application. This technique will generalize to any other Image Unit in the system, built in or otherwise.&lt;br /&gt;&lt;br /&gt;For the purposes of testing, I've created a simple Cocoa application called &lt;span style="font-family:courier new;"&gt;Convolver&lt;/span&gt;. As you can see, &lt;span style="font-family:courier new;"&gt;Convolver&lt;/span&gt; has one main window with two &lt;span style="font-family:courier new;"&gt;NSImageView&lt;/span&gt;s and a separate pallette with a 3x3 &lt;span style="font-family:courier new;"&gt;NSMatrix&lt;/span&gt; of coefficients. Dropping an image on the left view causes the right view to update with a processed version of the left image. Changing the coefficients immediately causes the filter to run and change the rightmost image.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bp2.blogger.com/_QGaeGVxB4Zg/RhxJmM-sm0I/AAAAAAAAACE/Gfiyf9qGYzE/s1600-h/convolver.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RhxJmM-sm0I/AAAAAAAAACE/Gfiyf9qGYzE/s400/convolver.png" alt="" id="BLOGGER_PHOTO_ID_5051993802670906178" border="0" /&gt;&lt;/a&gt;The structure of this application is pretty simple. There is an application controller called &lt;span style="font-family:courier new;"&gt;ConvolverController&lt;/span&gt; and a very simple model (&lt;span style="font-family:courier new;"&gt;Convolver&lt;/span&gt;) which only takes an unprocessed &lt;span style="font-family:courier new;"&gt;NSImage&lt;/span&gt; with an array of coeficients and returns a processed one. Taking a look at the nib file there are the user interface objects, the controller and the model all instantiated in the nib.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RhxL18-sm1I/AAAAAAAAACM/qiCtrPFCWK0/s1600-h/convnib.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RhxL18-sm1I/AAAAAAAAACM/qiCtrPFCWK0/s400/convnib.png" alt="" id="BLOGGER_PHOTO_ID_5051996272277101394" border="0" /&gt;&lt;/a&gt;The inspector for the &lt;span style="font-family:courier new;"&gt;ConvolverController&lt;/span&gt; object shows the outlets for the controller.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RhxMV8-sm2I/AAAAAAAAACU/aDSRsalJRvU/s1600-h/outlets.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RhxMV8-sm2I/AAAAAAAAACU/aDSRsalJRvU/s400/outlets.png" alt="" id="BLOGGER_PHOTO_ID_5051996822032915298" border="0" /&gt;&lt;/a&gt;The &lt;span style="font-family:courier new;"&gt;sourceImage, convolutionMatrix&lt;/span&gt; and the &lt;span style="font-family:courier new;"&gt;File-&gt;Open&lt;/span&gt; menu all have their targets set to the &lt;span style="font-family:courier new;"&gt;convolve:&lt;/span&gt; method of the controller, so that opening an image or changing the coefficients of the matrix will cause the image to be processed again.&lt;br /&gt;&lt;br /&gt;The Convolution controller class is very simple. Here's the interface for that class&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/* ConvolverController */&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import “Convolver.h“&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@interface ConvolverController : NSObject&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet NSMatrix *convolutionMatrix;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet Convolver *convolver;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet NSImageView *resultImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet NSImageView *sourceImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    IBOutlet NSWindow *window;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (IBAction)convolve:(id)sender;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (IBAction)openImage:(id)sender;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and here's the implementation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import “ConvolverController.h“&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@implementation ConvolverController&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (IBAction)convolve:(id)sender&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSImage *source = [sourceImage image];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSImage *dest = [convolver processImage: source &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        withCoefficients: [convolutionMatrix cells]];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [resultImage setImage: dest];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (IBAction)openImage:(id)sender&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSOpenPanel *panel = [NSOpenPanel openPanel];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [panel beginSheetForDirectory: nil&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                             file:nil&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                            types: [NSImage imageFileTypes]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                   modalForWindow: window &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                    modalDelegate:self &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                   didEndSelector:&lt;br /&gt;@selector(openPanelDidEnd:returnCode:contextInfo:)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                      contextInfo:nil];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (void)openPanelDidEnd:(NSOpenPanel *)panel&lt;br /&gt;   returnCode:(int)returnCode&lt;br /&gt;  contextInfo:(void  *)contextInfo{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSArray *files = [panel filenames];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSString *filename = [files objectAtIndex:0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSImage *image =&lt;br /&gt;[[[NSImage alloc]&lt;br /&gt;initByReferencingFile:filename] autorelease];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [sourceImage setImage: image];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [self convolve: self];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The&lt;span style="font-family:courier new;"&gt; convolve:&lt;/span&gt; method is called whenever the source image or coefficient matrix is changed. Notice that I pass the matrix cells unaltered to the model. At first I thought I would pull the information out of these cells and pass an array of &lt;span style="font-family:courier new;"&gt;NSNumber&lt;/span&gt;, but then I decided that I would just make sure that the model would take the &lt;span style="font-family:courier new;"&gt;floatValue:&lt;/span&gt; of whatever input it got to make sure I got the correct input type. Without strong typing, it seemed that I would have to do this in the model anyway, so I just do it there. The &lt;span style="font-family:courier new;"&gt;openImage:&lt;/span&gt; method opens a sheet to allow the user to select an image, and the&lt;span style="font-family:courier new;"&gt; openPanelDidEnd:returnCode:contextInfo:&lt;/span&gt; method sets the image file to the source image and calls convolve:&lt;br /&gt;&lt;br /&gt;The implementation of the &lt;span style="font-family:courier new;"&gt;Convolver&lt;/span&gt; class and the &lt;span style="font-family:courier new;"&gt;processImage:withCoefficients:&lt;/span&gt; method are the most important part of this exercise. Here's the header:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/* Convolver */&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@interface Convolver : NSObject&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    CIFilter *convolution;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSDictionary *filterAttributes;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    CIContext *context;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;-(NSImage *)processImage:(NSImage *)image&lt;br /&gt;withCoefficients:(NSArray *)coefficients;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and here's the implementation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;#import “Convolver.h“&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@implementation Convolver&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;-(id)init&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    if( self = [super init] ){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        [CIPlugIn loadAllPlugIns];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        convolution = [CIFilter filterWithName:&lt;br /&gt;                     @“Convolution3by3“];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        [convolution retain];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        filterAttributes = [[convolution attributes]&lt;br /&gt;                         retain];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    return self;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;The &lt;span style="font-family:courier new;"&gt;init&lt;/span&gt; method loads all the Core Image plugins in the system, and gets the Convolution3x3 filter from the system. We are not going to use the attributes but you can access them and use them if you wish by using the attributes message on the filter you load.&lt;br /&gt;&lt;br /&gt;The only other method in this class is &lt;span style="font-family:courier new;"&gt;processImage:withCoefficients&lt;/span&gt; and I'll break it down for you a step at a time.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;-(NSImage*)processImage:(NSImage*)image&lt;br /&gt;  withCoefficients:(NSArray*)coefficients&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    if( context == nil ){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        context = [CIContext contextWithCGContext:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            [[NSGraphicsContext currentContext]&lt;br /&gt;           graphicsPort]&lt;/span&gt;&lt;span style="font-family:courier new;"&gt; options:nil];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        [context retain];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first thing we need to do is to get a Core Image graphics context which we get from our current graphics context.&lt;br /&gt;&lt;br /&gt;Now, since we are using an &lt;span style="font-family:courier new;"&gt;NSImageView&lt;/span&gt; we need to convert the &lt;span style="font-family:courier new;"&gt;NSImage&lt;/span&gt; within into a &lt;span style="font-family:courier new;"&gt;CIImage&lt;/span&gt;. The way I am doing this is by using the &lt;span style="font-family:courier new;"&gt;initWithFocusedViewRect&lt;/span&gt;: method to get a bitmap representation of the &lt;span style="font-family:courier new;"&gt;NSImage&lt;/span&gt; and then using that bitmap to initialize a &lt;span style="font-family:courier new;"&gt;CIImage&lt;/span&gt; object with that bitmap. Personally I'm not sure why there are multiple image types in Cocoa (I'm sure someone at Apple would have the reason) but it's just something we have to deal with.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSSize size = [image size]; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [image lockFocus];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSRect imageRect =&lt;br /&gt;  NSMakeRect(0, 0, size.width, size.height);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSBitmapImageRep* rep = [[NSBitmapImageRep alloc]&lt;br /&gt;  initWithFocusedViewRect:imageRect];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [rep autorelease];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    CIImage *bitmap = [[CIImage alloc]&lt;br /&gt;                     initWithBitmapImageRep: rep];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [bitmap autorelease];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [image unlockFocus];&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next step is to set the parameters for the filter. We first call &lt;span style="font-family:courier new;"&gt;setDefaults&lt;/span&gt; to get the parameters in a known good state in case we don't want to set all of them. Core Image uses Key-Value coding to set all its parameters. Apple uses this technology so often, and it has turned out to be so useful for all kinds of applications that I don't know what we did before it. One minor annoyance is that we have to make &lt;span style="font-family:courier new;"&gt;NSNumber&lt;/span&gt; objects for each of the &lt;span style="font-family:courier new;"&gt;float&lt;/span&gt; parameters since Cocoa doesn't have any sort of automatic coercion like there is in Java 1.5.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [convolution setDefaults];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [convolution setValue:bitmap&lt;br /&gt;                forKey:@“inputImage“];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSArray *keys = [NSArray arrayWithObjects:&lt;br /&gt;   @“r00“, @“r01“, @“r02“,&lt;br /&gt;   @“r10“, @“r11“, @“r12“,&lt;br /&gt;   @“r20“, @“r21“, @“r22“, nil];&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;    NSEnumerator *en = [keys objectEnumerator];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    int i = 0;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSString *key;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    while( key = [en nextObject] ){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        NSNumber *param =&lt;br /&gt;     [NSNumber numberWithFloat:&lt;br /&gt;        [[coefficients objectAtIndex:i++] floatValue]];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        NSLog(@“key %@ index %d value %@“, key, i-1, param);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        [convolution setValue: param forKey: key];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    } &lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we get the value for the &lt;span style="font-family:courier new;"&gt;“outputImage”&lt;/span&gt; key which calls the &lt;span style="font-family:courier new;"&gt;outputImage&lt;/span&gt; method in the filter class and actually produce the result.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    CIImage *result =&lt;br /&gt;   [convolution valueForKey:@“outputImage“];&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we have to convert back to a NSImage. Unfortunately from what I can tell, there's no way to just get a bitmap representation out of a CIImage object. If anyone knows of a better way to do this, please leave a comment! So, we draw the CIImage into our NSImage object and return it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSImage *outputImage =&lt;br /&gt;    [[[NSImage alloc] init] autorelease];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [outputImage setSize: size];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [outputImage lockFocus];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [result drawInRect: imageRect&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;              fromRect: imageRect&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;             operation: NSCompositeSourceOver &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;              fraction:1.0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    [outputImage unlockFocus];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    return outputImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's the end of this tale. There is another angle on this same problem, however. Instead of converting images and calling filters, we can embed the Quartz Composer composition we developed as a test directly into our application by using a &lt;span style="font-family:courier new;"&gt;QCView&lt;/span&gt; and controlling our composition using the &lt;span style="font-family:courier new;"&gt;QCPatchController&lt;/span&gt;. Next time we'll &lt;a href="http://fromaremotevillage.blogspot.com/2007/04/cocoa-application-with-custom-core_29.html"&gt;reimplement this app using those techniques&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-2833196858975859639?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/2833196858975859639/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=2833196858975859639' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/2833196858975859639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/2833196858975859639'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/04/cocoa-application-with-custom-core.html' title='Cocoa application with custom Core Image filter 5: calling the filter from a Cocoa app.'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_QGaeGVxB4Zg/RhxJmM-sm0I/AAAAAAAAACE/Gfiyf9qGYzE/s72-c/convolver.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-2053791234985382976</id><published>2007-03-29T18:56:00.000-07:00</published><updated>2007-05-11T13:55:50.293-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='core image'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='cifilter'/><category scheme='http://www.blogger.com/atom/ns#' term='image unit'/><title type='text'>Cocoa Application with custom Core Image filter 4: packaging the filter as an Image Unit</title><content type='html'>Greetings to all from my remote village. This month is turning out to be incredibly busy, so I'm glad to finally be posting this entry. Last time in the village,  we introduced QuartzComposer to test the convolution filter. Now, it's time to package it into an Image Unit. In Core Image, an Image Unit is a way to provide an image processing function as a plug in so that any application in the system that uses Core Image can find and use its function.&lt;br /&gt;&lt;br /&gt;Xcode provides an easy way to get started by providing a project template for an “Image Unit Plug in for Objective-C” under “StandardApplePlugins” in the XCode project window.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/RgxviBWKytI/AAAAAAAAAB8/UzJiHLCJgeM/s1600-h/projectdialig.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/RgxviBWKytI/AAAAAAAAAB8/UzJiHLCJgeM/s400/projectdialig.jpg" alt="" id="BLOGGER_PHOTO_ID_5047531912643136210" border="0" /&gt;&lt;/a&gt;When this project is created, it already contains some stub Objective-C code, which we will never touch, An example kernel which will be deleted, and some .plist and .strings files for localization. There are two types of filters, executable and non-executable filters. Non-executable filters run entirely on the GPU and therefore there is no need for any objective-C code in the filter. A nonexecutable filter is determined if all “sample” calls in the filter are of the form  color = sample(someSrc, samplerCoord(someSrc)); Since the sample instructions in our filter are of the form: sample(src, (samplerCoord(src) + loc)) which does not match the nonexecutable form, we have an executable filter.&lt;br /&gt;&lt;br /&gt;For an executable filter, only the version number, filter class and filter name are read from the Description.plist file. The other parameters need to be defined in an Objective-C subclass of CIFilter. Here's the interface to that class:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;QuartzCore/QuartzCore.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@interface Convolution3by3: CIFilter&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  CIImage *inputImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r00;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r01;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r02;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r10;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r11;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r12;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r20;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r21;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  NSNumber *r22;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;- (NSArray *)inputKeys;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (NSDictionary *)customAttributes;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (CIImage *)outputImage;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@end&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, we declare our input parameters and override 4 methods: init, inputKeys, customAttributes, and outputImage.  The names inputImage and outputImage are defined by convention and are used with key-value coding to call your filter. Also, filter processing is done in the outputImage method so that the filter is processed lazily - results are only generated as requested. Here's the implementation of init:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#import “Convolution3by3.h“&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;@implementation Convolution3by3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;static CIKernel *convolutionKernel = nil;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (id)init&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  if(convolutionKernel == nil){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSBundle *bundle =&lt;br /&gt;     [NSBundle bundleForClass:[self class]];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSString *code = [NSString stringWithContentsOfFile: &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      [bundle pathForResource:@“Convolution3by3“ &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                       ofType:@“cikernel“]];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    NSArray *kernels = [CIKernel kernelsWithString:code];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    convolutionKernel = [[kernels objectAtIndex:0] retain];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  }    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  return [super init];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The init method loads the cikernel code out of the bundle and loads it into the static convolutionKernel variable. It is possible to define multiple kernels in a single cikernel file if you wanted to, but I would probably keep them separate in most cases.&lt;br /&gt;&lt;br /&gt;The inputKeys method simply returns an array of parameters for the filter:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;-(NSArray *)inputKeys&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  return [NSArray arrayWithObjects:@“inputImage“, &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            @“r00“, @“r01“, @“r02“,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            @“r10“, @“r11“, @“r12“,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            @“r20“, @“r21“, @“r22“, nil];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By far the longest method of the Image Unit is the customAttributes method where we return a NSDictionary containing a separate NSDictionary for each of the filter parameters. Here's what it looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (NSDictionary *)customAttributes&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      NSNumber *minValue = [NSNumber numberWithFloat:-10.0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      NSNumber *maxValue = [NSNumber numberWithFloat:10.0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      NSNumber *zero = [NSNumber numberWithFloat:0.0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      NSNumber *one = [NSNumber numberWithFloat:1.0];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;      return [NSDictionary dictionaryWithObjectsAndKeys:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;            [NSDictionary dictionaryWithObjectsAndKeys:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                @“NSNumber“, kCIAttributeClass,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                kCIAttributeTypeScalar,  kCIAttributeType,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                minValue, kCIAttributeSliderMin,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                maxValue, kCIAttributeSliderMax,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                zero, kCIAttributeDefault, nil],&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;             @“r00“,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;             ...&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This repeats for all 9 parameters, the only one that is different is r11, which is the center parameter. The value of this parameter defaults to 1, so that by default the filter returns its input unaltered.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;             ...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;             [NSDictionary dictionaryWithObjectsAndKeys:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                @“NSNumber“, kCIAttributeClass,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                kCIAttributeTypeScalar,  kCIAttributeType,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                minValue, kCIAttributeSliderMin,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                maxValue, kCIAttributeSliderMax,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;                zero, kCIAttributeDefault, nil],&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;             @“r22“,&lt;/span&gt; nil];&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the attributes of the filter include minimum and maximum slider values in case you wanted to build a dynamic user interface for the filter. These constants are all defined in CIFilter.h.&lt;br /&gt;&lt;br /&gt;The final method is the outputImage method. It is very simple:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;- (CIImage *)outputImage&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  CISampler *src=[CISampler samplerWithImage:inputImage];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;     return [self apply: convolutionKernel, src,&lt;br /&gt;   r00, r01, r02,&lt;br /&gt;   r10, r11, r12,&lt;br /&gt;   r20, r21, r22, nil];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All we do here is get a sampler for the input image and call apply: with the kernel and all the parameters. This call returns our resulting image.&lt;br /&gt;&lt;br /&gt;Once you've compiled this filter you can make it available to the system by placing it in the /Library/Graphics/Image Units folder, or for just your user in ~/Library/Graphics/Image Units. Next time we will see how we &lt;a href="http://fromaremotevillage.blogspot.com/2007/04/cocoa-application-with-custom-core.html"&gt;call this image unit from a Cocoa program.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-2053791234985382976?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/2053791234985382976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=2053791234985382976' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/2053791234985382976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/2053791234985382976'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_29.html' title='Cocoa Application with custom Core Image filter 4: packaging the filter as an Image Unit'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_QGaeGVxB4Zg/RgxviBWKytI/AAAAAAAAAB8/UzJiHLCJgeM/s72-c/projectdialig.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-5138995795242935643</id><published>2007-03-18T20:53:00.001-07:00</published><updated>2007-03-19T06:31:03.690-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mac os x'/><category scheme='http://www.blogger.com/atom/ns#' term='big nerd ranch'/><category scheme='http://www.blogger.com/atom/ns#' term='training'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>All the Cocoa programming that could be crammed into 5 days</title><content type='html'>I just spent the last week attending the &lt;a href="http://bignerdranch.com/classes/cocoa.shtml"&gt;Cocoa bootcamp&lt;/a&gt; from &lt;a href="http://www.bignerdranch.com/"&gt;Big Nerd Ranch&lt;/a&gt;. I have one thing I'd like to clear up before I get started. The hat is just a prop. That's right, when you go to WWDC and you see Aaron Hillegass walking around with his big texas style cowboy hat, he's doing it for marketing purposes. I guess it works, because when I decided to get some Cocoa training, I remembered Aaron and the hat walking around the Moscone center and on the back of his book: &lt;a href="http://www.amazon.com/gp/product/0321213149?ie=UTF8&amp;tag=fromaremotevi-20&amp;amp;linkCode=as2&amp;camp=1789&amp;amp;creative=9325&amp;creativeASIN=0321213149"&gt;Cocoa(R) Programming for Mac(R) OS X (2nd Edition)&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=fromaremotevi-20&amp;amp;l=as2&amp;o=1&amp;amp;a=0321213149" alt="" style="border: medium none  ! important; margin: 0px ! important;" border="0" height="1" width="1" /&gt;&lt;br /&gt;&lt;br /&gt;Now, I'm not normally a fan of technical training. Give me a book and a web browser and a project and I'm good. Of course this leads to interesting situations like the time I  reverse engineered the Windows virtual to physical address mapping structures while not knowing how to refresh an explorer window (It's F5, right?).&lt;br /&gt;&lt;br /&gt;I've been poking around in Cocoa for a while now, and though I feel like I made some decent progress, I'm really interested in this technology and wanted to get a comprehensive overview of it from an expert. Sometimes you just want to do things “right.” I figured that in 5 days I'd get a quick but comprehensive overview of most of the technologies in Cocoa from a well known expert in the technology.&lt;br /&gt;&lt;br /&gt;The course did not disappoint. We dove right in to code, with Aaron leading us through an example application immediately, before even lecturing. All the lectures were relatively short, and most of the time in the class was spent actually coding. We mostly followed along with the exercises in the book, but we were encouraged to explore and change things as we progressed. In four and one half days we covered nearly the entire book, as well as several additional chapters that were part of the course materials.&lt;br /&gt;&lt;br /&gt;In the evenings, most people chose to come back to the classroom and either continue to work on the exercises or to work on a personal project. Aaron typically stayed around until about 10PM answering questions and being generally helpful.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/Rf4KBhes35I/AAAAAAAAABo/1Ky152h2d34/s1600-h/IMG_1186.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/Rf4KBhes35I/AAAAAAAAABo/1Ky152h2d34/s400/IMG_1186.jpg" alt="" id="BLOGGER_PHOTO_ID_5043479653984821138" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:78%;"&gt;Out on our daily walk (image courtesy of &lt;a href="http://fluidicspace.com/"&gt;Paul Warren&lt;/a&gt;)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;The people who took the class came from all over the country and the UK and had varied backgrounds and skill levels, but overall, they were extremely competent and interesting folk. It was enjoyable to meet each of them. I was surprised at how many people were paying their own way to take this class, which was not an insignificant investment in personal time and money. I estimate that about half of the people were there on their own ticket.&lt;br /&gt;&lt;br /&gt;The price of the course covers ground transportation to and from the airport, lodging and all meals as well as a copy of Aaron's book and course materials. If you don't want to bring your own computer, a modern iMac will be provided for you to use.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/Rf4Lqhes36I/AAAAAAAAABw/Zy-EehRzWqI/s1600-h/IMG_1584.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/Rf4Lqhes36I/AAAAAAAAABw/Zy-EehRzWqI/s400/IMG_1584.JPG" alt="" id="BLOGGER_PHOTO_ID_5043481457871085474" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:78%;"&gt;View from my suite&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;The place we stayed,  &lt;a href="http://www.serenbe.com/home.html"&gt;Serenbe Southern Country Inn&lt;/a&gt;, was probably one of the nicest places I've ever stayed. My personal suite had a screened in porch, a kitchen, a large living room with fireplace, a king size bed, whirlpool tub and huge glass shower. They fed us three excellent meals each day, along with snacks and drinks as we wanted them through the day.&lt;br /&gt;&lt;br /&gt;I really benefited from this class. Some topics seem complex when you try to explore them on your own, but Aaron was able to make them very accessible. Cocoa Bindings had always seemed somewhat inscrutable to me, but now things are much clearer. The Undo Manager in Cocoa is amazing; it really showcases how a dynamic language can be leveraged to make something that can be hard in other frameworks really easy. And I now feel like I understand how to build custom Views - something that always seemed difficult to me but is actually quite straightforward now.&lt;br /&gt;&lt;br /&gt;The idea of a total immersion environment, with all details taken care of and few distractions, along with a very experienced and patient instructor, leads to a learning environment that encourages success.  I highly recommend this class to anyone who would like to learn Cocoa.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-5138995795242935643?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/5138995795242935643/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=5138995795242935643' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/5138995795242935643'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/5138995795242935643'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/03/all-cocoa-programming-that-could-be.html' title='All the Cocoa programming that could be crammed into 5 days'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_QGaeGVxB4Zg/Rf4KBhes35I/AAAAAAAAABo/1Ky152h2d34/s72-c/IMG_1186.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-3881125729409556507</id><published>2007-03-10T08:28:00.000-08:00</published><updated>2007-03-29T19:36:29.258-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='convolution'/><category scheme='http://www.blogger.com/atom/ns#' term='image processing'/><category scheme='http://www.blogger.com/atom/ns#' term='mac os x'/><category scheme='http://www.blogger.com/atom/ns#' term='core image'/><category scheme='http://www.blogger.com/atom/ns#' term='quartz composer'/><category scheme='http://www.blogger.com/atom/ns#' term='open gl'/><title type='text'>Cocoa Application with custom Core Image filter 3: Testing the kernel with QuartzComposer</title><content type='html'>In the last post I wrote a convolution kernel for Core Image. But these kind of things need to be tested. Now, we could package the filter inside an Image Unit (which I'll do in a future post) but that would mean we would have to recompile and install the filter each time we wanted to make a change. It would be much better if there was an interactive environment that could be used to test any changes and to allow us to debug the filter code.&lt;br /&gt;&lt;br /&gt;Luckily, Apple has provided just such an environment in &lt;a href="http://developer.apple.com/graphicsimaging/quartz/quartzcomposer.html"&gt;QuartzComposer&lt;/a&gt;. If you are not familiar, Quartz Composer is an amazing application that provides a visual programming environment that allows the creation of all kinds of visualizations. If you've seen the RSS screensaver. then you've seen a QuartzComposer composition. If you've seen a preview of Time Machine, the swirling galaxy in the background is a QuartzComposer composition. I'm not going to explore this amazing piece of software in depth here, I suggest you look at some of these websites for more information: &lt;a href="http://createdigitalmotion.com/qcblog/"&gt;Quartz Composer Journal &lt;/a&gt;  or &lt;a href="http://www.quartzcompositions.com/phpBB2/"&gt;www.quartzcompositions.com&lt;/a&gt; or &lt;a href="http://www.boinx.com/chronicles/category/quartz-compositions/?gclid=CJbQ4b3X5ooCFQkzgQodsBbCkg"&gt;boinx&lt;/a&gt;  or &lt;a href="http://eskatonia.net/qcblog/quartz-composer-for-vjs/"&gt;Quartonian&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As a basic introduction, a composition is defined by dragging &lt;a href="http://developer.apple.com/documentation/GraphicsImaging/Conceptual/QuartzComposer/qc_concepts/chapter_2_section_3.html#//apple_ref/doc/uid/TP40001357-CH202-BAJGGJBE%5C"&gt;patches&lt;/a&gt; onto the workspace and connecting them together graphically. Patches contain ports which represent parameters which are passed between the patches. The results are displayed in real time in the viewer window, so you get immediate feedback as you change things. As we'll see later, Quartz composer compositions can be embedded in a &lt;a href="http://developer.apple.com/documentation/GraphicsImaging/Reference/QuartzFramework/Classes/QCView_Class/Reference/Reference.html"&gt;QCView&lt;/a&gt; in your Cocoa apps, and controlled via Cocoa Bindings.&lt;br /&gt;&lt;br /&gt;I've provided a link to my &lt;a href="http://www.blueiris.us/blogDownloads/test.qtz"&gt;test.qtz&lt;/a&gt; composition which consists of four patches. The convolution patch is built by dragging a core image kernel patch onto the editor and then copying the text of the kernel into the patch. You can use the inspector to change the input parameters to adjust the coefficients and test out the composition.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/RfMQfxes3yI/AAAAAAAAAAw/2vzrJ0CnhYk/s1600-h/editor.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 378px; height: 293px;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/RfMQfxes3yI/AAAAAAAAAAw/2vzrJ0CnhYk/s400/editor.png" alt="" id="BLOGGER_PHOTO_ID_5040390546001813282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This is the editor window, showing the composition.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RfMQ5Res3zI/AAAAAAAAAA4/0nf0RKtH79U/s1600-h/inspector.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 351px;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RfMQ5Res3zI/AAAAAAAAAA4/0nf0RKtH79U/s400/inspector.png" alt="" id="BLOGGER_PHOTO_ID_5040390984088477490" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here's the inspector for the Convolution patch showing where you put the kernel code.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_QGaeGVxB4Zg/RfMReRes30I/AAAAAAAAABA/TSTJuvG0F_w/s1600-h/viewer.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 396px; height: 310px;" src="http://bp2.blogger.com/_QGaeGVxB4Zg/RfMReRes30I/AAAAAAAAABA/TSTJuvG0F_w/s400/viewer.png" alt="" id="BLOGGER_PHOTO_ID_5040391619743637314" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This is the viewer, showing the input image filtered by the edge detection filter defined in the Input Parameters to the Convolution filter.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/RfMSGxes32I/AAAAAAAAABQ/I63GOEqnLMs/s1600-h/input+parameters.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 273px; height: 406px;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/RfMSGxes32I/AAAAAAAAABQ/I63GOEqnLMs/s400/input+parameters.png" alt="" id="BLOGGER_PHOTO_ID_5040392315528339298" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;If you have an iSight camera or other video source, replace the Image Importer with a video input, and you will see the convolution filter applied to your video stream in real time. That's just extremely cool.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_QGaeGVxB4Zg/RfM02hes33I/AAAAAAAAABY/WYlLLsJrGAM/s1600-h/editorvideo.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_QGaeGVxB4Zg/RfM02hes33I/AAAAAAAAABY/WYlLLsJrGAM/s400/editorvideo.png" alt="" id="BLOGGER_PHOTO_ID_5040430519262437234" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here's the editor window with a Video Input instead or the Image Importer.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_QGaeGVxB4Zg/RfM1SBes34I/AAAAAAAAABg/UOzIkfTQ3_M/s1600-h/viewervideo.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_QGaeGVxB4Zg/RfM1SBes34I/AAAAAAAAABg/UOzIkfTQ3_M/s400/viewervideo.png" alt="" id="BLOGGER_PHOTO_ID_5040430991708839810" border="0" /&gt;&lt;/a&gt;And here's a view of my living room with edge detection. Notice the frame rate of nearly 60 frames per second on my Mac Book Pro.&lt;br /&gt;&lt;br /&gt;Using this basic composition, with some modifications, you should be able to test out any core image kernel you can come up with. Next time, &lt;a href="http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_29.html"&gt;I'll build this filter into an image unit that can be used from any application that uses Core Image filters&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-3881125729409556507?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/3881125729409556507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=3881125729409556507' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3881125729409556507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3881125729409556507'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_10.html' title='Cocoa Application with custom Core Image filter 3: Testing the kernel with QuartzComposer'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_QGaeGVxB4Zg/RfMQfxes3yI/AAAAAAAAAAw/2vzrJ0CnhYk/s72-c/editor.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-8295614976896267319</id><published>2007-03-09T10:52:00.000-08:00</published><updated>2007-03-09T10:56:36.199-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='big nerd ranch'/><category scheme='http://www.blogger.com/atom/ns#' term='training'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Off to Cocoa Bootcamp</title><content type='html'>Next week I'm headed off to Cocoa Bootcamp from &lt;a href="http://bignerdranch.com/classes/cocoa.shtml"&gt;Big Nerd Ranch&lt;/a&gt;. I've been poking around in Cocoa for a while and I know parts of it pretty well, but I've decided to get a solid foundation moving forward. From what I have heard from friends, their classes are excellent.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-8295614976896267319?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/8295614976896267319/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=8295614976896267319' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/8295614976896267319'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/8295614976896267319'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/03/off-to-cocoa-bootcamp.html' title='Off to Cocoa Bootcamp'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-7636522245566195985</id><published>2007-03-05T11:08:00.000-08:00</published><updated>2011-08-21T16:54:15.647-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='convolution'/><category scheme='http://www.blogger.com/atom/ns#' term='core image'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='open gl'/><title type='text'>Cocoa Application with custom Core Image Filter 2: Implementing the convolution kernel</title><content type='html'>I this post I'm going to implement our convolution filter as a &lt;a href="http://www.apple.com/macosx/features/coreimage/"&gt;Core Image&lt;/a&gt; kernel. Writing a Core Image kernel is relatively straightforward, as I think you'll see. Core Image kernels are written in a subset of the &lt;a href="http://www.opengl.org/documentation/glsl/"&gt;Open GL Shading language&lt;/a&gt;, which is basically just C with some added data types a few keywords and function calls. &lt;a href="http://developer.apple.com/documentation/GraphicsImaging/Reference/CIKernelLangRef/index.html#//apple_ref/doc/uid/TP40004397"&gt;Apple's Core Image Kernel language reference&lt;/a&gt; describes the subset that you can use and also the parts of OpenGL shading language that are not implemented. Of note, are the lack of arrays and structures and severe restrictions on looping and conditionals. The vector types like &lt;code&gt;vec3&lt;/code&gt; and &lt;code&gt;vec4&lt;/code&gt; which provide vector types (very convenient to hold the R,G,B,alpha of a pixel) and the &lt;code&gt;sampler&lt;/code&gt; which allows you to sample the image&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A Core Image kernel has but a single pixel as its output, and is therefore applied once for each pixel in the output. So, you can sample any input pixels from as many input images as you want to generate your output pixel. The filter has to be expressed as a mapping from any set of input pixels to each single output pixel. In our case, this is not a problem. 3x3 Convolution is a pretty natural fit for Core Image, since we only have to sample the 9 pixels immediately surrounding any output pixel. So the first step in our code is to declare the header of the kernel:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;kernel vec4 Convolution3by3(&lt;br /&gt;sampler src,&lt;br /&gt;float r00, float r01, float r02,&lt;br /&gt;float r10, float r11, float r12,&lt;br /&gt;float r20, float r21, float r22)&lt;br /&gt;{&lt;br /&gt;vec2 loc;&lt;br /&gt;vec4 result = vec4(0,0,0,1);&lt;br /&gt;//0,0 in my mind is left and up.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I declare a kernel called Convolution3by3. The kernel takes a src argument that represents the source image and 9 floats, which represent the 9 coefficients of the convolution. You can see that lack of support for arrays would make a 5x5 or 7x7 convolution quite cumbersome with this system. We also declare a &lt;code&gt;loc&lt;/code&gt; variable to hold our current location and a &lt;code&gt;vec4&lt;/code&gt; for the result.&lt;br /&gt;&lt;br /&gt;To perform the convolution, we need to sample the pixels, multiply them by the correct coefficient, and add that value to the result. We've made the conscious decision to maintain the alpha (transparency) value of the result pixel as the alpha value of the center input pixel. Here's the code for the first operation:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;loc = vec2(-1.0,1.0);&lt;br /&gt;vec4 p00 = unpremultiply(&lt;br /&gt;sample(src,(samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb = p00.rgb * r00;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What's going on here? First, we call &lt;code&gt;samplerCoord()&lt;/code&gt; to get the location that the current output pixel represents. Adding the loc to it allows us to grab the correct pixel in the matrix for this coeficient. Next, we call &lt;code&gt;sample()&lt;/code&gt;to get the actual value of the pixel at that location. Core Image gives us pixel information with premultiplied alpha, which means that any transparency has been already multiplied through any RGB values for the pixel. This is a useful optimization since it makes compositing simpler. Since we are going to be using the alpha of the center pixel as the output of the result, we need to reverse this to correctly calculate the convolution, thus the call to &lt;code&gt;unpremultiply()&lt;/code&gt;. Finally. we multiply the RGB values of the pixel by the coefficient for that pixel and add it to the result. This process is repeated for each sampled location.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;loc = vec2(0.0,1.0);&lt;br /&gt;vec4 p01 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p01.rgb * r01;&lt;br /&gt;&lt;br /&gt;loc = vec2(1.0,1.0);&lt;br /&gt;vec4 p02 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p02.rgb * r02;&lt;br /&gt;&lt;br /&gt;loc = vec2(-1.0,0.0);&lt;br /&gt;vec4 p10 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p10.rgb * r10;&lt;br /&gt;&lt;br /&gt;vec4 p11 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src)) ));&lt;br /&gt;result.rgb += p11.rgb * r11;&lt;br /&gt;result.a = p11.a;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice here that I copy the alpha from the input pixel to the result.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;loc = vec2(1.0,0.0);&lt;br /&gt;vec4 p12 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p12.rgb * r12;&lt;br /&gt;&lt;br /&gt;loc = vec2(-1.0,-1.0);&lt;br /&gt;vec4 p20 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p20.rgb * r20;&lt;br /&gt;&lt;br /&gt;loc = vec2(0.0,-1.0);&lt;br /&gt;vec4 p21 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p21.rgb * r21;&lt;br /&gt;&lt;br /&gt;loc = vec2(1.0,-1.0);&lt;br /&gt;vec4 p22 = unpremultiply(&lt;br /&gt;sample(src, (samplerCoord(src) + loc) ));&lt;br /&gt;result.rgb += p22.rgb * r22;&lt;br /&gt;&lt;br /&gt;result = premultiply( result );&lt;br /&gt;return result;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, I &lt;code&gt;premultiply()&lt;/code&gt; the result with the alpha value and return the result. As you can see, this is a pretty straightforward procedure: grab the values for each input pixel in the matrix, multiply them by their respective coefficients, accumulate the results and return.&lt;br /&gt;&lt;br /&gt;If you want to download a copy of this kernel, it's available on my website: &lt;a href="http://www.blueiris.us/blogDownloads/Convolution3by3.cikernel"&gt;Convolution3by3.cikernel&lt;/a&gt; In my next post I'll describe how to &lt;a href="http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_10.html"&gt;test the kernel using the QuartzComposer application and also show how to apply this filter to live video as well as static images&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-7636522245566195985?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/7636522245566195985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=7636522245566195985' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7636522245566195985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7636522245566195985'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_05.html' title='Cocoa Application with custom Core Image Filter 2: Implementing the convolution kernel'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-3040620594721790491</id><published>2007-03-01T08:10:00.000-08:00</published><updated>2007-03-05T11:21:23.331-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='convolution'/><category scheme='http://www.blogger.com/atom/ns#' term='image processing'/><title type='text'>Cocoa application with custom Core Image filter 1: What is image convolution?</title><content type='html'>Before I delve into development of my Core Image kernel, I think I would like to give a quick description of image convolution. There are plenty of resources on the internet that already describe the mathematics of this procedure in great detail, such as &lt;a href="http://www.ph.tn.tudelft.nl/Courses/FIP/noframes/fip-Convolut-2.html"&gt;Image Processing Fundamentals - Convolution-based Operations&lt;/a&gt;. A Google search will reveal a wealth of information.&lt;br /&gt;&lt;br /&gt;While it's easy to get lost in the mathematics, performing an image convolution filter is a relatively simple operation. Basically you apply a multiplier to a matrix of pixels surrounding each destination pixel and add them all together. The result of this operation is the new value of the pixel at the center. For the purposes of these articles, we will be sticking with a simple 3x3 matrix, but there is no reason why you can't perform this type of filtering with larger matrices.&lt;br /&gt;&lt;br /&gt;Here are some examples of 3x3 convolution applied to an image:&lt;br /&gt;&lt;br /&gt;This first example shows an edge detection algorithm&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/Reb9aQfja3I/AAAAAAAAAAU/cpk-_ivdM-o/s1600-h/edgeweb.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 329px; height: 137px;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/Reb9aQfja3I/AAAAAAAAAAU/cpk-_ivdM-o/s400/edgeweb.png" alt="" id="BLOGGER_PHOTO_ID_5036991860806216562" border="0" /&gt;&lt;/a&gt;This second one shows a sharpness kernel.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_QGaeGVxB4Zg/Reb9uQfja4I/AAAAAAAAAAc/7wXuPT5_vW8/s1600-h/sharpnessweb.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 336px; height: 131px;" src="http://bp0.blogger.com/_QGaeGVxB4Zg/Reb9uQfja4I/AAAAAAAAAAc/7wXuPT5_vW8/s400/sharpnessweb.png" alt="" id="BLOGGER_PHOTO_ID_5036992204403600258" border="0" /&gt;&lt;/a&gt;I find the easiest way to think about this is that the coefficients provide the weight that that particular pixel contributes to the final result. If you are interested in interactively exploring this concept, I recommend this site: &lt;a href="http://micro.magnet.fsu.edu/primer/java/digitalimaging/processing/convolutionkernels/index.html"&gt;Molecular Expressions Microscopy Primer: Digital Image Processing - Convolution Kernels - Interactive Java Tutorial.&lt;/a&gt; It's got some great interactive tutorials.&lt;br /&gt;&lt;br /&gt;In the next post I'll &lt;a href="http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core_05.html"&gt;build the Core Image kernel using the OpenGL shading language&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-3040620594721790491?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/3040620594721790491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=3040620594721790491' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3040620594721790491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3040620594721790491'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core.html' title='Cocoa application with custom Core Image filter 1: What is image convolution?'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_QGaeGVxB4Zg/Reb9aQfja3I/AAAAAAAAAAU/cpk-_ivdM-o/s72-c/edgeweb.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-7715872062335575193</id><published>2007-02-27T14:12:00.000-08:00</published><updated>2007-05-11T04:45:26.665-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='convolution'/><category scheme='http://www.blogger.com/atom/ns#' term='image processing'/><category scheme='http://www.blogger.com/atom/ns#' term='mac os x'/><category scheme='http://www.blogger.com/atom/ns#' term='core image'/><category scheme='http://www.blogger.com/atom/ns#' term='quartz composer'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><title type='text'>Building a Cocoa application using a custom Core Image filter (introduction)</title><content type='html'>A couple of months ago, some members of my team were working on a facial recognition project for one of our customers. One thing they were trying to do was to determine if filtering the images in various ways could improve the recognition algorithm.&lt;br /&gt;&lt;br /&gt;They were implementing their system in Java, but I immediately thought that Core Image would make their job much faster, since we could offload the image processing to the graphics cards on our Mac Pros rather than writing the algorithms in Java.&lt;br /&gt;&lt;br /&gt;If you are not familiar, &lt;a href="http://www.apple.com/macosx/features/coreimage/"&gt;Core Image&lt;/a&gt; is Apple's image processing framework which can provide very high performance image processing by offloading the work of image processing to the graphics card. Core Image includes a huge number of image filters (just check out the list on the Apple web page).&lt;br /&gt;&lt;br /&gt;The test system applied a sequence of image convolution filters to images with varying coefficients and then submitted the filtered images to the recognition system where they would be scored. We iterated over different combinations of filters to find a way to improve the scoring of the algorithm in general.&lt;br /&gt;&lt;br /&gt;I was surprised to discover that Apple did not include a convolution filter by default. Convolution is the "jack of all trades" of image processing algorithms and can be used for all sorts of things like edge detection, sharpening, blurring, noise reduction, and so on, depending on the coefficients used.&lt;br /&gt;&lt;br /&gt;This meant that I would have to develop my own custom image unit that performed convolution. This seemed like it might be a difficult task, but it turned out to be much easier than I expected. Along the way I learned a lot more about Quartz Composer, programming using the Open GL shading language, Cocoa Bindings, and creating custom image units on Mac OS X.&lt;br /&gt;&lt;br /&gt;Over the next several posts &lt;a href="http://fromaremotevillage.blogspot.com/2007/03/cocoa-application-with-custom-core.html"&gt;I'll step through the development process to a working sample Cocoa application that allows you to apply convolution to any image&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-7715872062335575193?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/7715872062335575193/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=7715872062335575193' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7715872062335575193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/7715872062335575193'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/02/building-cocoa-application-using-custom.html' title='Building a Cocoa application using a custom Core Image filter (introduction)'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9199573357345606334.post-3119587409589565525</id><published>2007-02-27T13:56:00.000-08:00</published><updated>2007-02-27T13:57:38.928-08:00</updated><title type='text'>Hello</title><content type='html'>Welcome to my blog. I'm a senior level engineer who's exploring developing software for OS X and using Cocoa. As I discover new things, I plan on posting my experience here with the humble hope that I might be able to help someone else and that other folks might be able to help me. &lt;br /&gt;&lt;br /&gt;Other things of interest to me might find their way here as well. We'll see.&lt;br /&gt;&lt;br /&gt;Thanks for tuning in, &lt;br /&gt;Paul&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9199573357345606334-3119587409589565525?l=fromaremotevillage.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fromaremotevillage.blogspot.com/feeds/3119587409589565525/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9199573357345606334&amp;postID=3119587409589565525' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3119587409589565525'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9199573357345606334/posts/default/3119587409589565525'/><link rel='alternate' type='text/html' href='http://fromaremotevillage.blogspot.com/2007/02/hello.html' title='Hello'/><author><name>Paul Franceus</name><uri>http://www.blogger.com/profile/11557290901923425436</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://blueiris.smugmug.com/photos/92203555-Th.jpg'/></author><thr:total>1</thr:total></entry></feed>
