Parmanoirhttp://parmanoir.com/Parmanoiren-usparmanoir@gmail.com2008-02-26T16:09:06-08:00daily1Less bugs through compiler optimizationshttp://parmanoir.com/Less_bugs_through_compiler_optimizationsStupid bug day !

// Get a symbol
void* sym = dlsym(handle, "function1");
// Cast it to correct signature
double (*fn)() = sym;
// Call it
fn();
// Get another symbol
void* sym2 = dlsym(handle, "function2");

// Cast it to correct signature
SomeStruct (*fn2)(float, float, float) = sym2;
// Call it
fn(1, 2, 3);

// Misassign new symbol to first pointer
fn = sym2;
// Call uninitialized function pointer
fn2(1, 2, 3);

And *BUG* ! Hmm … strange … let's try that in a Release build, one of my Stupid Voodoo Programming Tactiques, and … it works ! I stare at the code for a stupid amount of time, dumbfounded, wondering the obvious bug lies. Oh. There. I'm getting a new function pointer but calling the former one with the new signature … oups ! I'm getting a new function pointer but assigning it to the first one, then calling that uninitialized pointer.

How come it works in Release but not in Debug ? In Debug, GCC optimizes nothing and allocates two function pointers. In Release, GCC allocates one function pointer and uses it in both calls. Therefore calling fn(1, 2, 3) equals calling fn2(1, 2, 3); : that's Invisible Release Build Bug.

Moral of the day : always Release builds ! :)

EDIT I messed up while cleaning the code for this post. I even messed up the diagnostic, but … not the explanation ! :) To sum it up : XCode won't warn you when calling uninitialized function pointers.

]]>
Patrick Geiller2008-07-02T15:28:18
CocoaNav JS, a light CocoaNav for Safarihttp://parmanoir.com/CocoaNav_JS%2C_a_light_CocoaNav_for_SafariCocoaNav JS is a light version of CocoaNav. It's faster to use as the class list is static (dumped from CocoaNav), but also less powerful as you can't search methods or protocols, only classes. And sometimes it's beachballing and looking like it will crash Safari, but I assure you it won't ! Just let it run.

CocoaNav is Leopard only, and I see Tiger users and Windows users checking the page out :) If you're one of them, this will give you a quick way to discover the Cocoa Class Tree, each class having a link to Apple Developer.

CocoaNav JS

EDIT : turns out that slowdown occurs when I set the canvas's dimensions. I use it to draw lines between classes. On my Mac the layout produces a 1198x16000 table, the canvas is overlaid beneath and sized to cover it all. If Safari allocates one big plane, that comes up to a 73 Mb RGBA surface. Anyone got any canvas tips, or a better way ? I'm just drawing lines here.

]]>
Patrick Geiller2008-06-25T16:27:21
NSWindow goodies : bottomCornerRounded, usesLightBottomGradienthttp://parmanoir.com/NSWindow_goodies_:_bottomCornerRounded%2C_usesLightBottomGradientSome undocumented NSWindow methods to add sharp corners to textured windows, and an iPhoto/iTunes like bottom gradient bar.

Image:BottomCornerRounded usesLightBottomGradient.png

As this is undocumented, proceed with caution with respondsToSelector.

if ([window respondsToSelector:@selector(setBottomCornerRounded:)])
	[window setBottomCornerRounded:NO];
]]>
Patrick Geiller2008-06-23T15:56:52
Inspecting NSUndoManager's undo stackhttp://parmanoir.com/Inspecting_NSUndoManager%27s_undo_stackI had trouble understanding NSUndoManager and its retain policy so I wrote some code, and then I was enlightened :) There's a great article on ex-cinder.com that explains the concepts, so I won't go into detail - check the code out.

Contents

Two methods and their retain behaviours

When doing an undoable action, you register in NSUndoManager a method than undoes what you just did. It will be called when undoing. Depending on your method's arguments, call one of these :

  • registerUndoWithTarget registers a call with one ObjC object as parameter
  • prepareWithInvocationTarget registers a call with multiple parameters, even non ObjC objects like NSPoint

If you set a value from 4 to 5, register in the undomanager a call to set the value to 4, then set it to 5. If you add an object with addObject:, register a call to removeObject:. That's really all there is to it.

prepareWithInvocationTarget and registerUndoWithTarget both retain their arguments. Each undoable change ups the retain count. So when moving an object around, the retain count will go up at each position change. Don't worry if an object reaches a retain count of 30. Clearing the undo stack gets the retain count back to its initial value.

Peering through NSUndoManager

NSUndoManager does not offer any public way to view its undo and redo stacks. Thanks to F-Script, we can see that it has an undocumented _undoStack method, but nothing for the redo stack. NSUndoManager's header file shows 2 instance variables _redoStack and _undoStack. We'll fetch these with object_getInstanceVariable and call description on them, which dumps their contents.

Undoing adding and removing of objects in an NSArray

Storing objects in an array ups their retain count. Create an object (count=1), add it in an array (count=2). If you don't release the object (count=1) after adding it to the array, NSUndoManager will restore the object to its original retain count (1) when disposing of it, thus it will never be released. This is fine if you overload your objects' release method and plan on pooling them. If not, release your objects after adding them to the array or they will stick around forever.

Sample code

Image:iconZip.png Undo Inspector.zip

]]>
Patrick Geiller2008-06-22T17:16:39
Cocoa Regular Expressions via JavascriptCorehttp://parmanoir.com/Cocoa_Regular_Expressions_via_JavascriptCoreEver been stumped by the lack of regular expressions in Cocoa ? You can do quite a lot of complicated stuff in a few lines of code but comes the time to match a string to some pattern and then … where are regexes ? On your mac, they're living in PHP, Ruby, Perl, on webpages via WebKit, in some command line tools, but where in Cocoa ? Nowhere ! How about using WebKit ? Why not, but WebKit needs a NSWindow, a WebView, an html page with some code. Someone on CocoaDev suggested JavascriptCore, so we'll use just that !

Using JavascriptCore

To use JavascriptCore and get some results out of it :

  • create a context : JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
  • create a JS script string : JSStringRef scriptJS = JSStringCreateWithCFString((CFStringRef)@"var p = new RegExp(pattern, flags); string.match(p)");
  • evaluate our script and get the result : JSValueRef result = JSEvaluateScript(ctx, scriptJS, NULL, NULL, 0, NULL);
  • convert our Javascript result to a Cocoa string :
JSStringRef resultStringJS = JSValueToStringCopy(ctx, result, NULL);
CFStringRef resultString = JSStringCopyCFString(kCFAllocatorDefault, resultStringJS);
JSStringRelease(resultStringJS);
// Returns a Cocoa object by casting from a CFString
return	(id)resultString;

There's more as you have to do conversions to and from Cocoa. WebKit bridges strings, arrays, numbers but JavascriptCore does nothing, so we have to handle it ourselves.

From there we do a direct mapping of Javascript's match and replace.

Augmenting NSString

We'll define three methods :

  • match matches the first result found. Returns a string or nil. Equivalent to Javascript's string.match(/pattern/)
  • matchAll returns all matches. Returns a string array or nil. Equivalent to Javascript's string.match(/pattern/g)
  • replace matches the first result found. Returns a string or nil. Equivalent to Javascript's string.replace(/pattern/)

Given a sample string That is a sample STRING and a pattern of I[\w]+ (match capital I followed by one or more [a-zA-Z0-9]) :

// matches ING
- (id)matchWithPattern:(NSString*)pattern;
// matches ['ING']
- (id)matchAllWithPattern:(NSString*)pattern;
// returns That is a sample STR***
- (id)replaceWithString:(NSString*)replacement andPattern:(NSString*)pattern;

And case (in)sensitive versions :

// matches is
- (id)matchWithPattern:(NSString*)pattern isCaseSensitive:(BOOL)c;
// matches ['is', 'ING']
- (id)matchAllWithPattern:(NSString*)pattern isCaseSensitive:(BOOL)c;
// returns That *** a sample STR***
- (id)replaceWithString:(NSString*)replacement andPattern:(NSString*)pattern isCaseSensitive:(BOOL)c;

Sample code

Image:iconZip.png RegEx via JavascriptCore.zip NOTE this uses JavascriptCore for regular expressions, and WebView to display the results. That means there are actually two Javascript engines running :) one for matching, one for displaying. The displaying is of course completely optional, if you want to use regexes in your project copy RegExJS.m and RegExJS.h then add JavascriptCore.framework.

]]>
Patrick Geiller2008-06-16T12:54:24
Crossing the WebKit bridgehttp://parmanoir.com/Crossing_the_WebKit_bridgeUnfortunately the WebKit bridge is not as full featured as RubyCocoa's. You can call ObjC methods, use strings, numbers and arrays, and that's it. No C methods or structs, no derivating JS objects from ObjC objects. Following Using Objective-C From JavaScript describes the possibilities, the one thing of note is that calls like [someObject someMethod:@"hello" withAnotherParam:@"world"] are translated as someObject.someMethod_withAnotherParam_('hello', 'world') .

What's of note

  • NSDictionary is useless. Returning [NSDictionary dictionaryWithObjectsAndKeys:@"someString", @"key", nil] will yield a very strange JS object. You can display it with document.write(hashFromObjC) and you'll get what you expect : { key : someString }, but that hash is not usable at all ! Trying hash['key'] or hash.key gives nothing, trying to loop over it with for (name in hash) … gives nothing either. On the other direction, passing a hash to ObjC wil yield a WebScriptObject, not a NSDictionary. So, hashes are out.
  • Retain count. Exposing an object with [windowScriptObject setValue:theObject forKey:@"theObject"]; retains the object. Each ObjC method returning an object retains it. If you don't 'cache' the object you got and always do objCobjectExposedToJS.getObject().doStuff(), the retain count of the object returned by getObject will be incremented each time you call it. BUT getting an object in an array doesn't retain it.
  • No manual garbage collection start method. Internet explorer had GC.collect(), useful to check for memory leaks. In Safari, there's a WebCoreStatistics that's handling quite a lot of stuff, including garbage collection. Add a method collect to your JS-exposed object that calls [objc_getClass("WebCoreStatistics") garbageCollectJavaScriptObjects] and you'll get a manual way to start JS collection. Check WebCoreStatistics.mm in the WebKit source for a list of its methods, including interesting stuff like javaScriptObjectsCount.
  • Crossing stuff like NSPoint will have to be done with arrays.
#define NSPointToJS(p) [NSArray arrayWithObjects:[NSNumber numberWithFloat:p.x], [NSNumber numberWithFloat:p.y], nil]
#define NSPointFromJS(d) NSMakePoint([[d objectAtIndex:0] floatValue], [[d objectAtIndex:1] floatValue])
]]>
Patrick Geiller2008-06-15T14:11:25
Double and Triple Clickhttp://parmanoir.com/Double_and_Triple_ClickOK, that's pretty stupid :) but I had trouble figuring out what part of Cocoa handled double clicks. Turns out there's a clickCount message in NSEvent that will do just that.

Compared to Javascript :

JSObjC
mouse downonmousedownmouseDown
mouse uponmouseupmouseUp
clickonclickmouseUp, check [event clickCount]==1
double clickondblclickmouseUp, check [event clickCount]==2
triple click
paragraph selection
-mouseUp, check [event clickCount]==3


clickCount also lives for mousedown, so you can check things like triple mouse down. No great use I suppose but it's there.

]]>
Patrick Geiller2008-06-08T12:55:21
Photoshop-like compositing with Core Animationhttp://parmanoir.com/Photoshop-like_compositing_with_Core_AnimationIf you've ever tried Core Image's compositing filters, you've seen the BIG gotcha : the images are composited one of top of each other and you can't change their position. That means they're stuck at the same position, both cramped at (0, 0) — not very useful unless you're blending same size images. To position two images where you want them and have the top one composited on top of the other one, you have to compute the common rectangle they share, extract sub images from that rectangle, and composite them with the compositing filter. Ugh. Luckily, Core Animation has a compositingFilter property that will handle just that.

Image:Photoshop-like compositing with Core Animation.jpg

All it takes is myLayer.compositingFilter = [CIFilter filterWithName:@"CIAdditionCompositing"]; and myLayer will be compositing on top of the previous layer in the layer tree. Varying myLayer.opacity will give the same result as in photoshop, changing the opacity of the composited image.

There still is the limitation of the GPU texture size, so this won't handle big (2000+x2000+ ?) images. Maybe CATiledLayer can be of some help there ?

Sample code

Image:iconZip.png Blending Modes.zip

]]>
Patrick Geiller2008-06-05T16:37:29
One way binding to NSSliderhttp://parmanoir.com/One_way_binding_to_NSSliderI was trying to bind an NSSlider's value to another object. I figured it would be nice to set the slider's value in IB and have it act as default for the bound object. While binding the slider to the object ([myObject bind:@"opacity" toObject:opacitySlider withKeyPath:@"value" options:nil];), my code kept crashing with

*** Terminating app due to uncaught exception 'NSUnknownKeyException', 
reason: '[<NSSlider 0x125040> valueForUndefinedKey:]: this class is not key value coding-compliant for the key value.'

What ? NSSlider does not have a value key ? That's new ! It's there in IB, it shows up in its exposedBindings … Turns out only one way is supported : binding the view (slider) to the object.

BAD [myObject bind:@"opacity" toObject:opacitySlider withKeyPath:@"value" options:nil];

GOOD [opacitySlider bind:@"value" toObject: myObject withKeyPath:@"opacity" options:nil];

I already had that kind of crash with NSTextField. [slider valueForKey:@"value"] will crash, too. I suppose all the controls act alike, that means there's no KVC way to interact with them.

]]>
Patrick Geiller2008-06-05T15:25:50
Threaded Core Animation, Part Deuxhttp://parmanoir.com/Threaded_Core_Animation%2C_Part_Deux(Just for curiosity)

Thanks to its threading, Core Animation graphically updates itself when calling [CATransaction commit], even if we're in the middle of heavy work on the main thread. But how do you update standard NS* controls ?

With … [CATransaction commit], of course :) We'll work here with NSProgressIndicator. First, setup CA for that control. : [progress setWantsLayer:YES];. Then, in the middle of your heavy work :

[CATransaction begin];
[progress setDoubleValue:newValue];
// No refresh happens unless we change something, so dummy change opacity
[[progress layer] setOpacity:1];
[CATransaction commit];

And you'll see your control update. It's pretty useless because the progress animates itself in the main thread's idle time and we're eating all of it, so there's no animation ! Just the progress … progressing.

And it doesn't seem to work on WebView.

]]>
Patrick Geiller2008-05-30T14:10:55
A 2D Bevel Techniquehttp://parmanoir.com/A_2D_Bevel_TechniqueHow do you generate a 2D bevel out of a shape ? Maybe start by computing a heightfield, then light it just like Photoshop does … but then, how do you try that out ? Go push individual pixels in C ? Bwwwwarg.

Luckily after browsing the Core Image Reference, we can see that everything we need to do a bevel is there : CIHeightFieldFromMask and CIShadedMaterial.

Image:A 2D Bevel Technique.png

Once there, varying the radius on CIHeightFieldFromMask will vary the bevel depth.

Image:Varying Gaussian Blur Radius.png

Sample QTZ

Bevel 4.qtz

]]>
Patrick Geiller2008-05-28T12:26:48
Zero Opacity Trickhttp://parmanoir.com/Zero_Opacity_TrickIn CSS, some controls are hard to style : the file upload button has almost zero provision for styling. But you can make these controls transparent and add your custom styling beneath ! This becomes a simple matter of layering :

  • draw whatever kind of control you want in Photoshop
  • overlay a zero opacity control over it
  • then your custom control will behave like the one you're styling : the browser handles mouse clicks for transparent elements just as if they were fully visible.

And, we can do the same in Cocoa ! Namely, put a transparent control over a custom view and have the custom view behave as that transparent control.

Image:Zero Opacity Trick.png

In that case, I wanted to have that context menu pop exactly underneath that gray view. Couldn't figure out how to do it, tried the CSS zero opacity trick, and … it almost worked, trailing some repainted trash behind. After enabling Core Animation for the gray view, everything worked OK.

There must be some better way of doing that :), but it's pretty amazing to see how well JS+CSS concepts are close to Cocoa's.

]]>
Patrick Geiller2008-05-26T15:08:57
Sorry NNW readershttp://parmanoir.com/Sorry_NNW_readersIf you've seen old articles marked as new, that's because of the templates I use at the end of my posts. You'll notice at the end of a Core Animation post some more links to Core Animation : they are shared amongst all CA posts. When adding a new CA post, I'd update the template and old articles would get the new template version. I've now stripped those from the feed, so you shouldn't see anymore "old new" posts, save for just today, NNW notifying you that I've removed them. Thanks to Jonathan Wight for reporting the issue. Let me know if there are other problems.

Note that I occasionally fix the elusive typo, so you might get some false positives :)

]]>
Patrick Geiller2008-05-24T11:42:19
What's IB connecting to when you connect to First Responder ?http://parmanoir.com/What%27s_IB_connecting_to_when_you_connect_to_First_Responder_%3FTo nothing !

When first using Interface Builder, I was quite dumbfounded by the First Responder box sitting here. Image:First Responder.png What the hell is that '1' for ? Turns out it works almost like event bubbling in Javascript.

  • Event Bubbling when clicking an element on a page, the browser looks for onclick on that element. If not found, the browser goes up its ancestors until it finds an onclick it can respond to. If no handler is found, the browser uses its own onclick handler.
  • Responder chain when clicking an object in a window, Cocoa looks up action on that object. If that object has a target, that's like calling (in JS) clickedObject.target[clickedObject.action](clickedObject) (Sending itself as parameter, like event.target) — that's Target/Action in Cocoa terms. But if that object has no target, Cocoa goes up the responder chain to find an object that it can call its action on. If not found, you'll hear that default buzzing sound.

In JS we have a fixed name message searched up ; in Cocoa we have a variable name message searched up. Why ? To give each link in the chain a chance to handle the message. When selecting copy in the Edit menu, an action called copy: is sent down the chain. If NSTextField handles it, it copies its text to the pasteboard. If your control handles it, you get a chance to put whatever you want on the pasteboard. Another use is when using multiple NIBs (or a document based app) the first responder can be used to call methods on the application delegate, the last link of the responder chain.

So next time you're connecting an NSButton to myFancyMethod: in the first responder, Interface Builder will actually be setting your button's action property to myFancyMethod:. In the NIB it's connected to nothing — the 'connection' happens at runtime.

]]>
Patrick Geiller2008-05-23T13:08:43
Lanczos Scaling seems to handle Gamma wellhttp://parmanoir.com/Lanczos_Scaling_seems_to_handle_Gamma_wellIn Gamma error in picture scaling, Éric Brasseur shows the failings of modern image software to handle Gamma while scaling. I've tried it out scaling his sample image in Quartz Composer with a Lanczos Scaling filter, and it seems to look OK :

Image:Gamma qtz 1.png

On the left, OpenGL Scaling. This is meaningless but pretty, so it's included :)

On the right, Lanczos Scaling. The Dalai Lama remains visible even when scaled down to almost nothing.

By comparison, this is how Quartz Composer displays the image, failing to handle Gamma :

Image:Gamma qtz 2.png

Download Gamma Problem.qtz

]]>
Patrick Geiller2008-05-22T15:03:56
Core Animation Phantom Fadehttp://parmanoir.com/Core_Animation_Phantom_FadeHave you ever seen ghosting when creating layers and animating them to a new position ? I have, and I've been puzzled. In CocoaNav, the frameworks gradually move to the back, and once they're loaded they all move up front on the same plane. Once I messed up so bad to actually see Core Animation fading between the two positions !

Animating squares, this looks like that :

Image:Core Animation Phantom Fade.png

You're expecting layers to smoothly move to their new positions, but they're fading instead. Turns out it's because creation and animation are done in the same transaction. You're telling CA to create and animate, CA then tries to move from N objects to N+1 objects smoothly. It does that with a fade.

The fix

Do it in two transactions. Use a zero duration transaction for creating your layers, then animate them.

// Creation transaction, zero duration
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:0] forKey:kCATransactionAnimationDuration];
[self addRect:NSMakeRect(0, 50, 90, 100)];
[CATransaction commit];

// Animation transaction, whatever duration you want
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration];
[self animateLayers];
[CATransaction commit];

You'll then see what you expected : smooth animation to the new positions.

Sample Code

Image:iconZip.png CoreAnimationPhantomFade.zip

]]>
Patrick Geiller2008-05-21T18:11:52
CocoaNav 1.01http://parmanoir.com/CocoaNav_1.01Update :

  • Fixed a crash when switching views (Thanks to Axel M. Roest)
  • Added a link to Cocoadev - select a class, then command+enter to go the corresponding page

Image:iconZip.png CocoaNav 1.01

]]>
Patrick Geiller2008-05-21T11:50:55
Introducing CocoaNav, a Cocoa Class Browserhttp://parmanoir.com/Introducing_CocoaNav%2C_a_Cocoa_Class_BrowserCocoaNav is a project I've built to teach myself Cocoa. It parses frameworks in /System and displays them with Core Animation. And it's open source !

Download CocoaNav

Source

If some of you are motivated to help, leave a comment. The app is stable but the parsing sucks and does not understand C functions, defines, or structs — only Objective C classes. CocoaNav can parse your projects and display their classes in the Cocoa hierarchy (eg your NSView subclasses will shop up as descendant of NSView in the tree), but there is no refresh when you're editing your files. Apparently FSEvents can notify the file changes, now to handle them … Also only .h files are parsed, so any method defined in .m files won't show up.

Can CocoaNav be used to visually check things like Demeter's Law ? Or to graph which functions are called by whom how many times ? This would make a depth graph of function usage, if it's too messy this might be time to refactor ! I've never seen a way to visually glimpse the code flow of a project, that would be pretty cool if we could do that visually. With Core Animation, of course :)

]]>
Patrick Geiller2008-05-20T17:46:33
Exposing everything to WebKithttp://parmanoir.com/Exposing_everything_to_WebKitWhen using WebKit with Cocoa, you can expose any ObjC object to Javascript. But there's a catch : you need to specify which keys and selectors are allowed to be called via isKeyExcludedFromWebScript and isSelectorExcludedFromWebScript. But what if we want to expose everything ? Just add these methods to NSobject with a category :

@implementation NSObject(ExposeEverything)

+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector {
	return NO;
}
+ (BOOL)isKeyExcludedFromWebScript:(const char *)property {
	return NO;
}

@end

Now you can expose a dummy object that returns NSApp, and call in your JS code myExposedObject.NSApp().keyWindow().setHasShadow_(false). Pretty neat :), save for the trailing underscore.

Why expose everything ? I've tried RubyCocoa for Cocoa development and have had strange bugs (crashing when adding a comment), plus I don't really like Ruby as a language. It doesn't handle the increment operator ++, multiline comments, and has a strange syntax for blocks — compared to Javascript who uses the unterse function (arg1, arg2) everywhere for functions and lambdas. And Javascript's (scope | inheritance) model, where a (function/lambda | object) inherit its parent (scope | prototype), is just so elegant. I can say I grok Javascript where everything is pretty simple and powerful, even if it's limited, but Ruby just confuses me. I wonder how much development can be done in JS instead of ObjC or RubyCocoa … I'll be exploring.

]]>
Patrick Geiller2008-05-18T15:28:22
Projected coordinates of a 3D CALayerhttp://parmanoir.com/Projected_coordinates_of_a_3D_CALayerThere's no explicit method to get projected coordinates of a 3D CALayer, but you can use convertRect:fromLayer to almost get them. Thanks to Simon Fraser on the Quartz ML for that.

Suppose you have layers setup like that :

rootLayer
  layerHavingA3DTransform
    layer1
    layer2
    layer3

layer1, layer2, layer3 will be transformed by layerHavingA3DTransform and will appear 3D. To get their projected coordinates, use

CGRect projectedRect = [layer1 convertRect:localRect toLayer:[self layer]];

This will transform localRect, a rect in layer1 coordinates to root layer coordinates. If you want the layer extent, that's -bounds.width/2, -bounds.height/2, bounds.width, bounds.height for the default anchorPoint of (0.5, 0.5). Note that projectedRect is in layer coordinates, not in NSView coordinates. For this case I had manually centered layerHavingA3DTransform in the view and got coordinates reflecting that, going from -size to +size.

I only tested axis aligned layers. If you want coordinates from a rotated layer, you might try convertPoint:fromLayer to convert each layer point.

]]>
Patrick Geiller2008-05-14T13:20:36