<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.3.3" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>Vacuous Virtuoso &#187; Source Code</title>
	<link>http://lipidity.com</link>
	<description>Despotic Development</description>
	<pubDate>Tue, 23 Dec 2008 06:24:04 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.3.3</generator>
	<language>en</language>
			<item>
		<title>Quicksilver B5X branch</title>
		<link>http://lipidity.com/apple/quicksilver-b5x-branch/</link>
		<comments>http://lipidity.com/apple/quicksilver-b5x-branch/#comments</comments>
		<pubDate>Wed, 12 Dec 2007 23:10:12 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Quicksilver]]></category>

		<category><![CDATA[Application]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/quicksilver-b5x-branch/</guid>
		<description><![CDATA[Just committed my changes to the subversion repository.

A few new features and general stability improvements, but mostly a very thorough cleanup of the source code, so you can actually compile it now.

Compilation Instructions

Open Xcode preferences, scroll across to Source Trees and add one with a Setting Name of &#8220;QSFrameworks&#8221; and path of /Applications/Quicksilver.app/Contents/Frameworks/.

Then unzip Quicksilver.xcodeproj.zip, [...]]]></description>
			<content:encoded><![CDATA[<p>Just committed my changes to the <a href="http://blacktree-alchemy.googlecode.com/svn/branches/B5X/">subversion repository</a>.</p>

<p>A few <a href="http://lipidity.com/apple/cleaning-up-quicksilver/">new features</a> and general stability improvements, but mostly a very thorough cleanup of the source code, so you can actually compile it now.</p>

<h4>Compilation Instructions</h4>

<p>Open Xcode preferences, scroll across to Source Trees and add one with a Setting Name of &#8220;QSFrameworks&#8221; and path of /Applications/Quicksilver.app/Contents/Frameworks/.</p>

<p>Then unzip Quicksilver.xcodeproj.zip, and open the Quicksilver.xcodeproj project in Xcode. Make sure you set the Active Build Configuration to &#8220;Release&#8221;. Click the Build button and you&#8217;re away.</p>

<p><strong>Note:</strong> This build appears to be incompatible with the File Tagging plugin.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/quicksilver-b5x-branch/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Cleaning up Quicksilver</title>
		<link>http://lipidity.com/apple/cleaning-up-quicksilver/</link>
		<comments>http://lipidity.com/apple/cleaning-up-quicksilver/#comments</comments>
		<pubDate>Fri, 07 Dec 2007 08:38:31 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Quicksilver]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/cleaning-up-quicksilver/</guid>
		<description><![CDATA[Given the Quicksilver source code is now out in the open, I&#8217;m trying to learn from it while at the same time cleaning it up. Here&#8217;s what I&#8217;ve done so far.



Bug fixes

A lot of features were buried in the source, available only by setting some preferences to allow them. Mostly it was due to buggy [...]]]></description>
			<content:encoded><![CDATA[<p>Given the <a href="http://code.google.com/p/blacktree-alchemy/source">Quicksilver source code</a> is now out in the open, I&#8217;m trying to learn from it while at the same time cleaning it up. Here&#8217;s what I&#8217;ve done so far.</p>

<!--more-->

<h3>Bug fixes</h3>

<p>A lot of features were buried in the source, available only by setting some preferences to allow them. Mostly it was due to buggy implementation or pending improvements. I managed to resurrect a few of these nifty ones and fix a few old bugs that have annoyed me for ages.</p>

<h4>Color handling</h4>

<p>Color handling was commented out in the Quicksilver source; probably because it resulted in errors. A simple change in the source allowed correct handling of color values, and the code for drawing the icon was already there. I added functionality to allow dropping colors from the Quicksilver object pane into color wells and the like - which is what makes this useful.</p>

<p class="centre"><img src="http://images.lipidity.com/QS-Color-handling-20071207-185619.jpg" alt="Handling color in the object pane is now possible" title="A color stored in the object pane" /></p>

<h4>Uninstalled plugins</h4>

<p>Added a plugin set to the plugins preference pane to list plugins that you haven&#8217;t yet installed or downloaded. Not sure why this was commented out; I didn&#8217;t have to add any new code here.</p>

<p class="centre"><img src="http://images.lipidity.com/QS-Plug-ins-20071207-185845.jpg" alt="Uninstalled Plugins can now be listed" title="Listing uninstalled plugins in the preference pane" /></p>

<h4>Triggers</h4>

<p>There was a very annoying bug in the triggers preference pane that haunted me from the day I started using Quicksilver. Switching to the iTunes trigger set resulted in errors, and often froze or messed up Quicksilver. Finally, it&#8217;s fixed, and we can see and use these triggers.</p>

<p class="centre"><img src="http://images.lipidity.com/QS-Triggers-20071207-190435.jpg" alt="Quicksilver triggers preference pane has been fixed" /></p>

<p>Speaking of plugins; I think I heard mention of a &#8220;plugin check bug&#8221; fixed somewhere, but it wasn&#8217;t working for me so I&#8217;ve also fixed the issue where the plugin list would be blank until you hit refresh.</p>

<h4>Miscellaneous bug fixes</h4>

<p>Other things like crashing when you try to relaunch with the &#8220;Customize&#8221; preference pane open, and with the source, hundreds of warnings when trying to compile, errors in the shell scripts when working in a path with non-alphanumeric characters, etc. have also been fixed.</p>

<p class='centre'><img src="http://images.lipidity.com/QS-Customize-20071207-190804.jpg" alt="Fixed a crash with the Quicksilver customize pane" /></p>

<h3>Tidy up</h3>

<blockquote>
  <p>[I wonder] how long it&#8217;ll take people to notice the Frankensteinian nature of the source and respond  - appropriately - with pitchforks. <cite><a href="http://twitter.com/alcor/statuses/388774142" rel="nofollow">Alcor</a></cite></p>
</blockquote>

<p>I&#8217;ll have to agree with Alcor - to say the source is a mess would be an understatement. There are hundreds of compiler warnings, unused or unimplemented features, classes, objects, code, structures, nibs, etc. Hundreds. I&#8217;ve tried to remove as much of the redundant or test code as possible and I&#8217;ve yet to reach the end. Mind you, I&#8217;ve been doing this for hours a day for the past week. The formatting and indentation is almost as bad. I&#8217;m cleaning that up too, with some good old regular expressions and elbow grease. As I go, I&#8217;m also trying to improve the logic where I see potential for improvement. Already the application is feeling more responsive but I want to also reduce the memory footprint.</p>

<p>Why go to all this trouble? Mainly, to</p>

<ul>
<li>Make the source more readable; make it easier for people to understand what&#8217;s going on.</li>
<li>Improve the performance of the application in both speed and memory usage.</li>
<li>Fix existing bugs and add new features.</li>
</ul>

<hr />

<p>In the meantime, I&#8217;ll continue my clean up of the Quicksilver source and when it&#8217;s lookin&#8217; fine perhaps post it up (the QS license doesn&#8217;t really say much about the source, so I might have to ask Alcor first. Perhaps he&#8217;d like to add it to the subversion repository after reviewing?).</p>

<p>Lastly, let me just say, I&#8217;ve learnt a lot from looking at the source code to Quicksilver. There are some very neat tricks in there, and great little snippets that could be very useful elsewhere. So thank you Mr. Jitkoff; thank you for writing the application, and thank you for the releasing the source code. It is highly appreciated.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/cleaning-up-quicksilver/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Another guide to Quicksilver</title>
		<link>http://lipidity.com/apple/another-guide-to-quicksilver/</link>
		<comments>http://lipidity.com/apple/another-guide-to-quicksilver/#comments</comments>
		<pubDate>Tue, 04 Dec 2007 10:49:13 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Quicksilver]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Review]]></category>

		<category><![CDATA[Source Code]]></category>

		<guid isPermaLink="false">http://lipidity.com/general/another-guide-to-quicksilver/</guid>
		<description><![CDATA[Joe Dunn is doing a nice series of articles on Quicksilver. He&#8217;s covered triggers and scripts so far with more on the way.

One thing I disagree with, though, is the excessive use of the &#8220;do shell script&#8221; AppleScript. I like the &#8220;Run Command in Shell&#8221; action just fine. But if you want an icon, writing [...]]]></description>
			<content:encoded><![CDATA[<p>Joe Dunn is doing a nice series of articles on Quicksilver. He&#8217;s covered <a href="http://jwdunn.com/2007/11/24/how-i-use-quicksilver-i-triggers/">triggers</a> and <a href="http://jwdunn.com/2007/11/29/how-i-use-quicksilver-ii-scripts/">scripts</a> so far with more on the way.</p>

<p>One thing I disagree with, though, is the excessive use of the &#8220;do shell script&#8221; AppleScript. I like the &#8220;Run Command in Shell&#8221; action just fine. But if you want an icon, writing an Obj-C wrapper isn&#8217;t too much work. Especially since you have <a href="http://pub.lipidity.com/CocoaShell.tar.bz2">this source code</a>.</p>

<p>I&#8217;ve also got a bit more on Quicksilver coming up shortly.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/another-guide-to-quicksilver/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Modifier flags during app launch</title>
		<link>http://lipidity.com/apple/modifier-flags-launch/</link>
		<comments>http://lipidity.com/apple/modifier-flags-launch/#comments</comments>
		<pubDate>Tue, 27 Nov 2007 10:45:57 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/modifier-flags-launch/</guid>
		<description><![CDATA[Unfortunately, [[NSApp currentEvent] modiferFlags] can&#8217;t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.



The GetCurrentEventKeyModifiers() call works on Mac OS X v10.2 and later through which one can check for shiftKey, cmdKey, optionKey and controlKey.

I use the something [...]]]></description>
			<content:encoded><![CDATA[<p>Unfortunately, <code>[[NSApp currentEvent] modiferFlags]</code> can&#8217;t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.</p>

<!--more-->

<p>The <code>GetCurrentEventKeyModifiers()</code> call works on Mac OS X v10.2 and later through which one can check for <code>shiftKey</code>, <code>cmdKey</code>, <code>optionKey</code> and <code>controlKey</code>.</p>

<p>I use the something like the following to check if the shift key is pressed during launch for <a href="http://lipidity.com/software/zippit/">my compression app</a>.</p>

<pre><code>#import &lt;Cocoa/Cocoa.h&gt;
#import &lt;Carbon/Carbon.h&gt;

int main(int argc, char *argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    if((GetCurrentEventKeyModifiers() &amp; shiftKey)!=0) {
        // Change something useful
        NSLog(@"Shift key pressed during launch");
    }
    [[NSApplication sharedApplication] run];
    [pool release];
    return 0;
}
</code></pre>

<p>In the highly unlikely event that you&#8217;ve just got a call to <code>NSApplicationMain</code> in your <code>main</code> function (joking), you can just test <code>(GetCurrentEventKeyModifiers() &amp; shiftKey)!=0</code> in the <code>-applicationWillFinishLaunching:</code> method in your NSApplication delegate.</p>

<p>Any new APIs in Leopard to achieve this or any easier way?</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/modifier-flags-launch/feed/</wfw:commentRss>
		</item>
		<item>
		<title>CTGradient code bloat</title>
		<link>http://lipidity.com/apple/ctgradient-code-bloat/</link>
		<comments>http://lipidity.com/apple/ctgradient-code-bloat/#comments</comments>
		<pubDate>Tue, 20 Nov 2007 21:05:12 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Review]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/cgradient-code-bloat/</guid>
		<description><![CDATA[While getting rid of extraneous junk in an application package is easy using Trimmit, the only way to prevent &#34;code bloat&#34; (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let&#8217;s take CTGradient as an example as it&#8217;s well known [...]]]></description>
			<content:encoded><![CDATA[<p>While getting rid of extraneous junk in an application package is easy using <a href="http://lipidity.com/software/trimmit/">Trimmit</a>, the only way to prevent &quot;code bloat&quot; (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let&#8217;s take CTGradient as an example as it&#8217;s well known and used (or more accurately, abused) in dozens of applications.</p>

<!--more-->

<p>CTGradient contains an incredible diversity of built-in gradients, gradient styles, and methods for creating fancy rainbows, radial gradients, linear gradients, aqua gradients, and a number of other interesting class and instance methods. It allows you to dynamically alter gradients by adding or removing colors, changing the level of transparency, filling NSRects or NSBezierPaths, rotating the gradient, etc. For demonstration purposes, all these features are excellent. For production, this is a nightmare.</p>

<p><strong><a href="http://pub.lipidity.com/CTGradient/CTGradient.m">CTGradient.m</a> weighs in at over 1300 lines of code.</strong></p>

<p>Ignoring Mr. Weider&#8217;s unique style of formatting, take a look through the code. If you&#8217;re putting this class in an application, you can immediately remove over a thousand lines. Just like that. But if you take some time to understand what&#8217;s going on, you can optimize this thing till it runs like a Ferrari.</p>

<p>You&#8217;ll notice <a href="http://lipidity.com/software/trimmit/">Trimmit</a> uses a gradient background for it&#8217;s window. Let&#8217;s cut down CTGradient until it matches the level of optimization of Trimmit&#8217;s gradient code.</p>

<h2>Clear the junk</h2>

<p>Firstly, let&#8217;s remove the methods we know for sure we won&#8217;t need. Remove the following methods completely from both the interface and the implementation (scroll down for more):</p>

<pre style='height:340px;overflow:scroll'><code>// We don't need the preset gradients

+ (id)dividerGradient;
+ (id)statusBarGradient;
+ (id)aquaSelectedGradient;
+ (id)aquaNormalGradient;
+ (id)aquaPressedGradient;
+ (id)unifiedSelectedGradient;
+ (id)unifiedNormalGradient;
+ (id)unifiedPressedGradient;
+ (id)unifiedDarkGradient;
+ (id)sourceListSelectedGradient;
+ (id)sourceListUnselectedGradient;
+ (id)rainbowGradient;
+ (id)hydrogenSpectrumGradient;

// We won't make any drastic modifications to the gradient once created

- (CTGradient *)gradientWithAlphaComponent:(float)alpha;
- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position;
- (CTGradient *)removeColorStopAtIndex:(unsigned)index;
- (CTGradient *)removeColorStopAtPosition:(float)position;
- (CTGradientBlendingMode)blendingMode;
- (NSColor *)colorStopAtIndex:(unsigned)index;
- (NSColor *)colorAtPosition:(float)position;

// Now we don't need to conform to the NSCopying and NSCoding protocols

- (id)copyWithZone:(NSZone *)zone;
- (void)encodeWithCoder:(NSCoder *)coder;
- (id)initWithCoder:(NSCoder *)coder;

// We only need to fill a simple NSRect

- (void)drawSwatchInRect:(NSRect)rect;
- (void)radialFillRect:(NSRect)rect;
- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle;
- (void)radialFillBezierPath:(NSBezierPath *)path;

// Remove the entire (Private) category;
// but leave -addElement:

- (void)_commonInit;
// move setBlendingMode code into init
- (void)setBlendingMode:(CTGradientBlendingMode)mode;
- (CTGradientElement *)elementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtPosition:(float)position;
</code></pre>

<p>The following C functions are now unused:</p>

<pre><code>static void chromaticEvaluation(void *info, const float *in, float *out);
static void inverseChromaticEvaluation(void *info, const float *in, float *out);
static void transformRGB_HSV(float *components);
static void transformHSV_RGB(float *components);
static void resolveHSV(float *color1, float *color2);
</code></pre>

<p>And also remove the following from the header:</p>

<pre><code>typedef enum  _CTBlendingMode
    {
    CTLinearBlendingMode,
    CTChromaticBlendingMode,
    CTInverseChromaticBlendingMode
    } CTGradientBlendingMode;
</code></pre>

<p>Remove the <code>&lt;protocols&gt;</code> and unnecessary instance variables so the interface looks like this:</p>

<pre><code>@interface CTGradient : NSObject {
    CTGradientElement* elementList;
    CGFunctionRef gradientFunction;
}
+ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end;
- (void)fillRect:(NSRect)rect angle:(float)angle;
- (void)addElement:(CTGradientElement *)newElement;
@end
</code></pre>

<p>Before we go further, we&#8217;ll need to take a detour and fix the remaining code so that it compiles. That&#8217;s easy enough - just remove any references to code we&#8217;ve removed.</p>

<p>If you&#8217;ve been following along, your CTGradient should now look like <a href="http://pub.lipidity.com/CTGradient/CTGradient2.m">this</a> (also made it readable!).</p>

<p>In a few short minutes, we&#8217;re down from more than <strong>1300</strong> to a little over <strong>200 lines</strong>.</p>

<p><big>It gets better.</big></p>

<h2>Optimize it</h2>

<p>First stop, <code>fillRect:angle:</code>. Since the gradient for Trimmit&#8217;s window only runs vertically (angle 90), we can straight away take out the <var>angle</var> argument, and also cut the entire if / else structure with the angle down to the two lines that are under the <code>if(angle == 90)</code>. Ah, much better.</p>

<p>Now things start getting a teeny bit more complex - and fun.</p>

<hr />

<p>The CTGradient code is written so that one can have many different color stops. The CTGradientElement struct has a nextElement which points to the next CTGradientElement and the position of the current element is stored under the float <var>position</var>. However, we need just two - the starting and ending shades.</p>

<p>Let&#8217;s take a look at cutting the multiple elements down to just two.</p>

<p>The <code>addElement:</code> method has a lot of looping to insert the element at the correct place. However, since we know that we only ever need two, we can cut it down to this:</p>

<pre><code>- (void)addElement:(CTGradientElement *)newElement {
    if(elementList) {
        // elementList exists, add second element
        elementList-&gt;nextElement = malloc(sizeof(CTGradientElement));
        *(elementList-&gt;nextElement) = *newElement;
        elementList-&gt;nextElement-&gt;nextElement = 0;
    } else {
        // no elements - add first element
        elementList = malloc(sizeof(CTGradientElement));
        *elementList = *newElement;
        elementList-&gt;nextElement = 0;
    }
}
</code></pre>

<p>Similarly with <code>dealloc</code>,</p>

<pre><code>- (void)dealloc {
    CGFunctionRelease(gradientFunction);
    free(elementList-&gt;nextElement);
    free(elementList);
    [super dealloc];
}
</code></pre>

<p>While we&#8217;re at it, let&#8217;s clean up <code>init</code> as well:</p>

<pre><code>- (id)init {
    if(self = [super init]) {
        CGFunctionCallbacks evaluationCallbackInfo = {0 , &amp;linearEvaluation, 0};
        static const float input_value_range[2] = { 0, 1 };
        static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
        gradientFunction = CGFunctionCreate(&amp;elementList, 1, input_value_range, 4, output_value_ranges, &amp;evaluationCallbackInfo);
    }
    return self;
}
</code></pre>

<p>Now we head to the <code>linearEvaluation</code> function. <var>info</var> passed in refers to <var>elementList</var> (see the CGFunctionCreate call). Again, since we know that we&#8217;ll only ever have two elements we can reduce it to:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    float position = *in;

    CTGradientElement *color1 = *(CTGradientElement **)info;
    CTGradientElement *color2 = color1-&gt;nextElement;

    out[0] = (color2-&gt;red - color1-&gt;red)*position + color1-&gt;red; 
    out[1] = (color2-&gt;green - color1-&gt;green)*position + color1-&gt;green;
    out[2] = (color2-&gt;blue - color1-&gt;blue)*position + color1-&gt;blue;
    out[3] = (color2-&gt;alpha - color1-&gt;alpha)*position + color1-&gt;alpha;
}
</code></pre>

<p>And now we can finally remove the lines from <code>+gradientWithBeginningColor:endingColor:</code> where the positions are set, and also remove the <var>position</var> float from the CTGradientElement struct.</p>

<hr />

<p>We&#8217;re now down to a little over <strong>100 lines</strong>. Your CTGradientElement.m should now be looking something like <a href="http://pub.lipidity.com/CTGradient/CTGradient3.m">this</a>. Going well, but let&#8217;s take things up a notch.</p>

<hr />

<p>For Trimmit&#8217;s background, we&#8217;re not interested in the red, green, blue and alpha components - we just need a grayscale shading. This is the most fun part.</p>

<p>Instead of <code>float red, green, blue, alpha;</code> in the CTGradientElement struct, we can have just <code>float shade;</code>.</p>

<p>Now, during <code>init</code> we have:</p>

<pre><code>CGFunctionCallbacks evaluationCallbackInfo = {0 , &amp;linearEvaluation, 0};
static const float input_value_range[2] = { 0, 1 };
static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
gradientFunction = CGFunctionCreate(&amp;elementList, 1, input_value_range, 4, output_value_ranges, &amp;evaluationCallbackInfo);
</code></pre>

<p>The third argument, <var>input_value_range</var> is the domain. This gets passed into linearEvalution through <var>*in</var>.This is the independent variable. When we&#8217;re drawing the gradient, linearEvaluation is called with <var>*in</var> (the domain) starting at the first value of <var>input_value_range</var> and ending up at the second. So, something like 0.000, 0.001, 0.002 &#8230; 0.998, 0.999, 1.000. The function&#8217;s job is to set the the color components for each given value of the domain.</p>

<p>The first optimization we can make here is to return only one channel instead of four. This changes it to:</p>

<pre><code>- (id)init {
    if (self = [super init]) {
        CGFunctionCallbacks evaluationCallbackInfo = {0, &amp;linearEvaluation, 0};
        static const float range[2] = {0, 1};
        static const float domain[2] = {0, 1};
        gradientFunction = CGFunctionCreate(&amp;elementList, 1, domain, 1, range, &amp;evaluationCallbackInfo);
    }
    return self;
}
</code></pre>

<p>This change impacts back on the linearEvaluation function - now it only needs to return one channel:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    CTGradientElement *color1 = *(CTGradientElement **)info;
    out[0] = (color1-&gt;nextElement-&gt;shade - color1-&gt;shade)*(*in) + color1-&gt;shade;
}
</code></pre>

<p>And don&#8217;t forget <code>+gradientWithBeginningColor:endingColor:</code>. We improve performance here, as we only need the shade - not a color. We can rename it to something appropriate.</p>

<pre><code>+ (id)gradientWithBeginningShade:(float)begin endingShade:(float)end {
    id newInstance = [[[self class] alloc] init];
    CTGradientElement color1, color2;

    color1.shade = begin; color2.shade = end;

    [newInstance addElement:&amp;color1];
    [newInstance addElement:&amp;color2];
    return [newInstance autorelease];
}
</code></pre>

<p>Now we&#8217;re only returning one grayscale channel. But hold on, we&#8217;re still in the RGB colorspace! That&#8217;s easily fixed. In <code>fillRect:</code>, replace:</p>

<pre><code>#if MAC_OS_X_VERSION_MAX_ALLOWED &gt;= MAC_OS_X_VERSION_10_4
    CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
#else
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
#endif
</code></pre>

<p>with:</p>

<pre><code>CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
</code></pre>

<p class="note">CGColorSpaceCreateDeviceGray is device-dependent on Mac OS X 10.3 and below, but starting from Tiger, it&#8217;s now device-independent which is good in terms of appearance.</p>

<p>While we&#8217;re in <code>fillRect:</code>, we can also make a few more improvements:</p>

<pre><code>- (void)fillRect:(NSRect)rect {
    CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextSaveGState(currentContext);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
    CGContextClipToRect(currentContext, *(CGRect *)&amp;rect);
    CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, CGPointMake(0, 0), CGPointMake(0, NSMaxY(rect)), gradientFunction, 0, 0);
    CGContextDrawShading(currentContext, myCGShading);

    CGShadingRelease(myCGShading);
    CGColorSpaceRelease(colorspace);
    CGContextRestoreGState(currentContext);
}
</code></pre>

<p>I moved the startPoint and endPoint CGPoints straight into the CGShadingCreateAxial call. Also, we only ever fill the whole view with the gradient, so just put in <code>0</code> instead of using NSMinX and NSMinY. We could replace the <code>CGPointMake(0, 0)</code> with <code>(CGPoint){0,0}</code>, but I&#8217;m not sure if that&#8217;d be noticeably faster.</p>

<hr />

<p>Our code is at a lean <strong>80 lines</strong>. Now for the biggest change yet.</p>

<hr />

<p>Heading back to dear old linearEvaluation. Look at this line:</p>

<pre><code>out[0] = (color1-&gt;nextElement-&gt;shade - color1-&gt;shade)*(*in) + color1-&gt;shade;
</code></pre>

<p>What&#8217;s happening here? Think about it like this:</p>

<p>Essentially, we&#8217;re just &quot;graphing&quot; a straight line:</p>

<pre><code>y = mx + c
</code></pre>

<p>where <var>x</var> is the position, <var>*in</var>, and <var>y</var> is the shade we return (<var>out[0]</var>), and <var>c</var> is the initial shade.</p>

<p>Since our <var>x</var> value only goes from 0 to 1 (see domain in CGFunctionCreate), at <code>x = 0</code>, we will have <code>y = c</code>. At <code>x = 1</code>, we&#8217;ll have <code>y = m + c</code>.</p>

<p><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/gradient-graph.png' alt='Graph of Gradient Function' class='feature' /></p>

<p><strong>So</strong> we only need the start shade as our <var>*info</var> value, since we can have <var>m</var> as the difference between the final and initial shades. Let&#8217;s effect this in our code.</p>

<p>We can get rid of the CTGradientElement struct. Remove from the header:</p>

<pre><code>typedef struct _CTGradientElement {
    float shade;
    struct _CTGradientElement *nextElement;
} CTGradientElement;
</code></pre>

<p>Instead of the struct, we&#8217;ll use a single <var>shade</var> instance variable. We don&#8217;t need the <code>+gradientWithBeginningShade:endingShade:</code> anymore, nor do we need <code>-addElement:</code>. So our interface now looks like this:</p>

<pre><code>@interface CTGradient : NSObject {
    float shade;
    CGFunctionRef gradientFunction;
}
- (void)fillRect:(NSRect)rect;
@end
</code></pre>

<p>From the implementation, we can remove the <code>free</code> calls in <code>dealloc</code> as well as the whole of <code>+gradientWithBeginningShade:endingShade:</code> and <code>addElement:</code>. We can set the <code>shade</code> in the init method (and replace <code>&amp;elementList</code> with <code>&amp;shade</code>), and change the linearEvaluation function to:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    out[0] = *(float*)info + (*in)*0.1;
}
</code></pre>

<p>The <code>0.1</code> is what determines how much brighter the top of the gradient is (it&#8217;s the <var>m</var> value of our straight line) compared to the bottom.</p>

<p><strong>But</strong> since the <code>(*in)*0.1</code> will go from <code>0</code> to <code>0.1</code> (as <var>*in</var> goes from 0 to 1), we can actually change the <code>domain</code> we declare for CGFunctionCreate to {0, 0.1} and have our function as:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    out[0] = *(float*)info + *in;
}
</code></pre>

<p>You can now add accessors for <code>shade</code> if you need to change the shade of the gradient or you could even have an <code>initWithShade:</code> method. In fact, here&#8217;s an Xcode project with the gradient.</p>

<p class="download"><a href="http://pub.lipidity.com/CTGradient/GradientSample.tar.bz2">Lean Gradient Xcode Project</a></p>

<p>We now have our gradient code down <big>from <strong>1300 lines</strong> to just over <strong>30 lines</strong></big>.</p>

<hr />

<p>We&#8217;ve almost reached a similar level of optimization to Trimmit&#8217;s gradient. There will be further optimizations you can make, but it depends on what you&#8217;re using the gradient for. For example, if the gradient you&#8217;re drawing is always the same, you can optimize further by removing the <code>shade</code> instance variable and all references to it, and hardcode the value into the linearEvaluation function.</p>

<p>Getting to the end of this article, you&#8217;re probably thinking that it would be a bit of a waste to use CTGradient if by the time you optimize it for your application, it doesn&#8217;t resemble the original at all. And you&#8217;re quite right. The documentation already shows you <a href="http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_shadings/chapter_9_section_6.html">how to draw gradients</a>, yet the number of applications using CTGradient - the whole 1300 lines of it - is astonishing.</p>

<p>Please: When you use other people&#8217;s code, don&#8217;t put it in without a thought. Go through it, understand it, and optimize it for your specific need. For the better performance and reduced RAM usage, computers will thank you.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/ctgradient-code-bloat/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Now this is hot Cocoa</title>
		<link>http://lipidity.com/apple/now-this-is-hot-cocoa/</link>
		<comments>http://lipidity.com/apple/now-this-is-hot-cocoa/#comments</comments>
		<pubDate>Sun, 15 Apr 2007 12:43:08 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Download]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[smoke]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/feature/tutorial/now-this-is-hot-cocoa</guid>
		<description><![CDATA[It's finished. The real craze and controversy over <a href="http://discoapp.com" title="Disco App">Disco</a>'s smoke is over. <a href="http://toxicsoftware.com/blog/second-hand-smoke/" title="Disco smoke">Jonathan Wight</a> was the first to write up an article on integrating the smoke effects into a third party Cocoa application. Times have changed, and the smoke framework bundled with the more recent versions of the application no longer produce the desired results by using the same code.

So how do you get any Cocoa application to start smoking? <a href="http://www.austinsarner.com/" title="Austin Sarner">Austin Sarner</a> was kind enough to reveal the usage and implementation of the changes to the Disco framework which allow a wider variety of Macs to experience the entertaining effect.]]></description>
			<content:encoded><![CDATA[<h2>The Framework</h2>

<p>The framework (in more ways than one) for our smoking window is the Smoke framework embedded inside the Disco app. In your Xcode project, you can reference the framework straight from within the application. You&#8217;ll have to press Cmd-Shift-G and type in the path as you can&#8217;t navigate into bundles using the standard open file dialog. Normally the location is &#8220;/Applications/Disco.app/Frameworks/&#8221; inside which is the Smoke.framework bundle which we&#8217;re interested in.</p>

<p>Alternatively, you can copy the framework to /Library/Frameworks/ and reference it from there. If you need to modify the framework (more on that later) it&#8217;s best to choose this option so you leave the contents of the Disco application alone.</p>

<h2>Head to the headers</h2>

<p><a href='http://dev.lipidity.com/wp-content/uploads/2007/04/smoke.h' title='Download Smoke.h'><img src='http://dev.lipidity.com/wp-content/uploads/2007/04/obj-c-header.png' alt='Header icon' class='fright' /></a>Before we can use the framework, we need to have a valid header or header files for the framework, which tell Xcode (and us) mainly about the classes and methods in Smoke.framework. The headers aren&#8217;t included in the framework anymore, but to <a href="http://dev.lipidity.com/apple/exposing-those-private-frameworks" title="Generate header for framework">generate a header</a> file is a simple matter of using a utility like class-dump on the framework.</p>

<p><a class='callout' href='http://dev.lipidity.com/wp-content/uploads/2007/04/smoke.h' title='Download Smoke.h'>
Download Smoke.h:</a></p>

<p>You can take a look inside the header and see a bit of what&#8217;s possible with the framework. When you&#8217;re ready, we&#8217;ll move onto the&#8230;</p>

<h2>Setup</h2>

<p>Now we&#8217;ve got the framework and header in place, we need to code to actually get a window to smoke. On the surface, this is astonishingly simple.</p>

<p>In your Xcode project, make sure you&#8217;ve added Smoke.framework and Smoke.h as well as Quartz.framework as the smoke needs to use this. Now create a new Objective-C subclass. In this example we&#8217;ve used an NSWindow subclass, but you could just as easily subclass NSObject and only apply smoke on selected outlets. If you really wanted to start a bonfire, you could add a category to NSWindow just for smoking!</p>

<p>Firstly, we define everything in our header file. Our class is called SmokingWindow (SmokeWindow is taken by Smoke.framework) and we have four methods.</p>

<p>The header file will look something like this:</p>

<pre><code>//
//  SmokingWindow.h
//  Smoke
//
//  Created by Ankur Kothari on 13/04/07.
//  Copyright 2007 Vacuous Virtuoso. All rights reserved.
//

#import &lt;Cocoa/Cocoa.h&gt;
#import &lt;Quartz/Quartz.h&gt;
#import "Smoke.h"

@interface SmokingWindow : NSWindow {
    SmokeController *theSmokeController;
    int smoking;
}
-(int)smoking;
-(IBAction)startSmoking:(id)sender;
-(IBAction)stopSmoking:(id)sender;
-(IBAction)toggleSmoke:(id)sender;
@end
</code></pre>

<ul>
<li><code>-smoking</code> returns whether (1) or not (0) the window is currently smoking. It&#8217;s not needed to smoke, but it&#8217;s good practice.</li>
<li><code>-startSmoking</code> obviously starts the window smoking, and</li>
<li><code>-stopSmoking</code> stop the window smoking</li>
<li><code>-toggleSmoke</code> will stop the window smoking if it is, or start it smoking if it isn&#8217;t.</li>
</ul>

<p>We&#8217;re storing the current state of the window for use by the <code>-smoking</code> method in the int smoking, and <code>theSmokeController</code> is a pointer to a <code>SmokeController</code> which will be doing most of the work.</p>

<hr />

<p>Ok, now to the implementation.</p>

<pre><code>//
//  SmokingWindow.m
//  Smoke
//
//  Created by Ankur Kothari on 13/04/07.
//  Copyright 2007 Vacuous Virtuoso. All rights reserved.
//

#import "SmokingWindow.h"


@implementation SmokingWindow

-(void)awakeFromNib
{
    SmokeCapabilities *smokeCapabilities = [SmokeCapabilities sharedCapabilities];
    theSmokeController = [[SmokeController alloc] init];
    [theSmokeController doExpensiveThreadedInitializations];
    [theSmokeController setSmokeAlgorithm:[smokeCapabilities smokeAlgorithm]];
    [theSmokeController attachToWindow:self];
    smoking = 0;
    [theSmokeController showWindow:self];
}

-(IBAction)toggleSmoke:(id)sender{
    if(smoking){
        [self stopSmoking:sender];
    } else {
        [self startSmoking:sender];
    }
}

-(IBAction)startSmoking:(id)sender
{
    if(!smoking){
        [theSmokeController startSmoking:self];
        smoking = 1;
    }
}

-(IBAction)stopSmoking:(id)sender
{
    if(smoking){
        [theSmokeController stopSmoking:self];
        smoking = 0;
    }
}

-(int)smoking{
    return smoking;
}
@end
</code></pre>

<hr />

<p>The first few lines in <code>-awakeFormNib</code> get the SmokeController do some initialization.</p>

<pre><code>SmokeCapabilities *smokeCapabilities = [SmokeCapabilities sharedCapabilities];
theSmokeController = [[SmokeController alloc] init];
[theSmokeController doExpensiveThreadedInitializations];
[theSmokeController setSmokeAlgorithm:[smokeCapabilities smokeAlgorithm]];
</code></pre>

<p><br />
To tell the SmokeController which window to &#8216;burn&#8217;, the <code>-attachToWindow:</code> method is used. Since we&#8217;re using an NSWindow subclass in this example, we can just set it to <code>self</code>, otherwise you&#8217;ll need a pointer or outlet to an instance of NSWindow.</p>

<pre><code>[theSmokeController attachToWindow:self];
</code></pre>

<p><br />
To just make extra sure that the position of the smoke wont be offset if the window is dragged around during initialization, we call the <code>-showWindow</code> method. This isn&#8217;t completely necessary.</p>

<pre><code>[theSmokeController showWindow:self];
</code></pre>

<p><br />
To heat or cool the window, theSmokeController responds to <code>-startSmoking</code> and <code>-stopSmoking</code>. Easy enough!</p>

<h2>More fun!</h2>

<p>For extra smoking fun, you can add bindings to your application to control many aspects of the smoke. You can adjust the initial temperature, the amount of pressure exerted by the mouse, the <abbr title="Frames per second">FPS</abbr>, and many more. Just check the UserDefaults.plist file located in the Resources folder in Smoke.framework.<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup></p>

<h2>Hacking the smoke framework</h2>

<p>To get even more out of the smoke framework, you can edit various files within the framework itself. Most of the files are in the &#8220;Resources&#8221; folder. One of the most useful ones to edit is called boundary.cikernal. In the first two lines you can specify the height and width of the OpenGL view that the smoke appears in.</p>

<h3>Enjoy the smoke!</h3>

<h2>Sample project</h2>

<p><a href='http://pub.lipidity.com/Smoke.tar.bz2' title='Smoke project'><img src='http://dev.lipidity.com/wp-content/uploads/2007/04/xcode-project.png' alt='Xcode icon' /></a></p>

<hr />

<p><ins><strong>Developments</strong></ins></p>

<p>Austin has also graciously agreed to let me develop <a href="http://dev.lipidity.com/fumo" title="Fumo Quicksilver interface">Fumo</a>, the smoking Quicksilver interface.</p>

<p>Although there is still some talk about the <abbr title="Human Interface Guidelines">HIG</abbr>, usability, eye candy and so on, the controversy phase is mostly over. Speaking of which, you can call me what you like, but I  think the <abbr title="Human Interface Guidelines">HIG</abbr> is definitely <em>not</em> dead. Outdated perhaps, but there needs to be a standard. There are many aspects to the existing <abbr title="Human Interface Guidelines">HIG</abbr> that developers need to follow in order to make the Mac experience enjoyable. I&#8217;ll talk about this in detail later. <a href="http://dev.lipidity.com/apple/apple-hig-still-relevant">My rant about the <abbr title="Human Interface Guidelines">HIG</abbr></a>.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:1">
<p>One of the really good ones is MouseSmokeRadius, which makes smoke comes out of your mouse. I like to call them &#8220;fireballs&#8221;.&#160;<a href="#fnref:1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/now-this-is-hot-cocoa/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Warp, bend, squeeze and transform windows with CGSSetWindowWarp</title>
		<link>http://lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp/</link>
		<comments>http://lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp/#comments</comments>
		<pubDate>Wed, 28 Feb 2007 08:40:38 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Animation]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp</guid>
		<description><![CDATA[How would you like to have the power to squish, warp and transform windows to your liking? With the private Core Graphics function CGSSetWindowWarp, you can!]]></description>
			<content:encoded><![CDATA[<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/Warped.png" style="width: 100%" title="Warped clocks" alt="Warped clocks" /></p>

<p>The function CGSSetWindowWarp is part of the private portion of <a href="http://dev.lipidity.com/feature/tutorial/ebook-xcode-animations-core-graphics" title="Core Graphics tutorial ebook">Core Graphics</a>. Although its just as obscure and undocumented as the rest of the <ins>private parts of the</ins> framework, there has been some discovery and progress into its <a href="http://kevin.sb.org/articles/2006/07/23/cgssetwindowwarp-explained" rel="external">usage</a>.  Erling Ellingsen has written (a while ago) about CGSSetWindowWarp on <a href="http://blog.medallia.com/2006/05/windowwarp.html" rel="external">his blog</a>, and he also shows some very interesting screenshots of what&#8217;s possible.</p>

<p><img src="http://blog.medallia.com/images/warp/full-3d-fancy-thumb.jpg" alt="transformed windows" /><img src="http://blog.medallia.com/images/warp/full-3d-slash-thumb.jpg" alt="transformed windows" /></p>

<p>So, how do you use this incredible function in your Cocoa (<a href="http://paste.lisp.org/display/2717">or Carbon</a>) application?
<!--more--></p>

<h2>CGSSetWindowWarp Usage</h2>

<h3>Including the header</h3>

<p>Whenever you use private Core Graphics functions, you generally need to <code>#import </code> either one of CoreGraphicsServices.h or CGSPrivate.h (available for download with my <a href="http://dev.lipidity.com/feature/tutorial/ebook-xcode-animations-core-graphics" title="Core Graphics tutorial ebook">Core Graphics ebook</a>). However, if you&#8217;re not interested in using the rest of the Core Graphics API, you can get away with simply adding the following to your main class&#8217; header file:</p>

<pre class="code"><code>
typedef struct CGPointWarp {
        CGPoint local;
        CGPoint global;
}CGPointWarp;

typedef int CGSConnectionID;
typedef int CGSWindowID;

extern CGSConnectionID _CGSDefaultConnection();

extern CGError CGSSetWindowWarp(CGSConnectionID, CGSWindowID, int w, int h, CGPointWarp mesh[w][h]);
extern OSStatus CGSGetScreenRectForWindow(CGSConnectionID cid, CGSWindowID wid, CGRect *outRect);
extern OSStatus CGSGetWindowBounds(CGSConnectionID cid, CGSWindowID wid, CGRect *bounds);</code></pre>

<p>As you&#8217;ve probably guessed, the function that actually applies the mesh transform is <code>CGSSetWindowWarp</code>. You can create a mesh in two different ways.</p>

<h3>Creating the mesh</h3>

<p>To create a mesh, we need to specify how many points we&#8217;re going to take for our grid length-wise and width-wise. Something like this:</p>

<pre class="code"><code>#define H 64
#define W 5</code></pre>

<p>We&#8217;ll also need to refer to the position and size of the frame so the following code in the method you&#8217;re writing this will give us access to the frame of the NSWindow:</p>

<pre class="code"><code>CGRect frame;
CGSGetWindowBounds(cid, [window windowNumber], &amp;frame);</code></pre>

<p>The use of CGSGetWindowBounds ensures we get the apparent frame rather than just the frame computed by our code as we&#8217;ll be applying various transformations.</p>

<p>The first way to create a mesh requires more labor (yes, its tiring to code) but gives you greater control over the mesh and the transformations applied to the NSWindow.</p>

<h4>Manual mesh</h4>

<pre class="code"><code>CGPointWarp meshes[H][W] =
{
{ {{0, 0},{10, 10}}, {{200,0},{180,12}} },
{ {{0, 50},{0,50}}, {{200,50},{200,50}} },
{ {{0,100},{0,100}}, {{200,100},{200,100}} },
{ {{0,150},{0,150}}, {{200,150},{200,150}} },
{ {{0,200},{0,200}}, {{200,200},{200,200}} },
};</code></pre>

<p>Here, you specify firstly the coordinates of the point which you&#8217;re taking, and where you want to move that point. The first line in the above code takes (0,0) and moves it to (10,10) and moves (200,0) to (180,12). However, the first set of coordinates are local coordinates, and the second set are global. (ie., the first set are on the plane of the window, while the second set use the screen as the plane. This is to allow the window to be stretched out beyond the scope of the current frame.) We could solve this issue by using:</p>

<pre class="code"><code>
int x = frame.origin.x;
int y = frame.origin.y;
CGPointWarp meshes2[H][W] =
{
{ {{0, 0},{x, y}}, {{200,0},{x+200,y}} },
{ {{0, 50},{x,y+50}}, {{200,50},{x+200,y+50}} },
{ {{0,100},{x,y+100}}, {{200,100},{x+200,y+100}} },
{ {{0,150},{x,y+150}}, {{200,150},{x+200,y+150}} },
{ {{0,200},{x,y+200}}, {{200,200},{x+200,y+200}} },
};</code></pre>

<p>The preceding code just warps the window of size 200&#215;200 to its current position so no change is observable. If we wanted to stretch out the middle of the window horizontally, we could use a mesh in the form:</p>

<pre class="code"><code>
int x = frame.origin.x;
int y = frame.origin.y;
CGPointWarp stretchThroughMiddleMesh[H][W] =
{
{ {{0, 0},{x, y}}, {{200,0},{x+200,y}} },
{ {{0, 50},{x-10,y+50}}, {{200,50},{x+200+10,y+50}} },
{ {{0,100},{x-20,y+100}}, {{200,100},{x+200+20,y+100}} },
{ {{0,150},{x-10,y+150}}, {{200,150},{x+200+10,y+150}} },
{ {{0,200},{x,y+200}}, {{200,200},{x+200+10,y+200}} },
};</code></pre>

<p>We&#8217;ve taken the side where x=0 and moved x further back along the middle, and done the opposite on the opposite side (where x=200). This results in the NSWindow being warped to have a bulge in the middle.
<a href="http://dev.lipidity.com/wp-content/uploads/2007/02/warpbulge.png" title="warp bulge" rel="lightbox"><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warpbulge.thumbnail.png" alt="warp bulge" /></a></p>

<p>Again, this would only look like the screenshot on a window with a height and width of 200. For a window that&#8217;s a different size, you&#8217;ll need to change the values to match the height and width of the window. For the y-values in the mesh (x, x+50, x+100, etc. in the different rows), the numbers are determined by how many points you&#8217;ve taken (your value for <var>H</var>) and the width of the window. The general formula you would use would be <code>x + ( frame.size.height / (W-1) )</code>. You&#8217;ll see this more clearly in the second method.</p>

<h4>Mesh through iterative formulae</h4>

<p>The second method to create a mesh is to run a loop to configure your individual points. Here&#8217;s a basic example that creates a mesh that would be identical to the current state of the window:</p>

<pre class="code"><code>
int h;
int w;
CGPointWarp meshes[H][W];
for (h = 0; h &lt; H; h++) {
    for (w = 0; w &lt; W; w++) {
        CGPointWarp point;
        point.local.x = w * (frame.size.width / (W - 1));
        point.local.y = h * (frame.size.height / (H - 1));
        point.global.x = point.local.x + frame.origin.x;
        point.global.y = point.local.y + frame.origin.y;

        meshes[h][w] = point;
    }
}</code></pre>

<p>Obviously we want to transform the window somehow. We need to apply some sort of mathematical function to the points to make them deviate from their original positions or coordinates. (This is the part where you start to wish you&#8217;d paid more attention in math class).</p>

<p>Lets say we want to skew the window using the x-axis. Each successive x-value needs to be greater than the previous one. Since we&#8217;re looping through each point, at any time in the inner for loop, the values of the current point are (w,h). (In <a href="http://www.google.com/search?q=Cartesian+coordinates">Cartesian coordinates</a> the x value is first, followed by the y value) In order to increase each successive value for x, we need to add something. But, we need to add more than what we did the previous time. The easiest way to do this without adding any new variables is to use the value that we&#8217;re incrementing each time - the x value itself. So, for a skewed window, we have the following mesh:</p>

<pre class="code"><code>
int h;
int w;
CGPointWarp meshes[H][W];
for (h = 0; h &lt; H; h++) {
    for (w = 0; w &lt; W; w++) {
        CGPointWarp point;
        point.local.x = w * (frame.size.width / (W - 1));
        point.local.y = h * (frame.size.height / (H - 1));
        point.global.x = point.local.x + frame.origin.x + <span style="color: black">15*h</span>;
        point.global.y = point.local.y + frame.origin.y;

        meshes[h][w] = point;
    }
}</code></pre>

<p>The skewed window would like this when the mesh was applied:
<a href="http://dev.lipidity.com/wp-content/uploads/2007/02/skew.png" title="Skews CGSWarp" rel="lightbox"><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/skew.thumbnail.png" alt="Skews CGSWarp" /></a></p>

<h3>Applying the mesh</h3>

<p>The transform mesh was really the hard part. Applying the transformation mesh (or grid) to a window is as easy as:</p>

<pre class="code"><code>
CGSSetWindowWarp(_CGSDefaultConnection(), [window windowNumber], W, H, meshes);</code></pre>

<p>The possibilities of this are really amazing. I&#8217;ve always wanted to write an NSWindow subclass that bent and reduced in opacity as it was dragged, imitating <a href="http://static.flickr.com/72/221355329_a7dc022e74.jpg" rel="external">XGL</a>. With CGSSetWindowWarp and Core Graphics, I&#8217;m sure it&#8217;ll be a breeze. Or what about an virtual universe-style Finder cross Desktop, sort of like <a href="http://www.acm.uiuc.edu/macwarriors/projects/oldprojects/3dosx/screenshots.html" rel="external">3DOSX</a>? You could even imitate other effects with this, such as the cube, but still get live updating in your windows - a limitation present in the Core Graphics transition effects.</p>

<p>For a source code, and a further explanation, see Kevin Ballard&#8217;s post on the subject of <a href="http://kevin.sb.org/articles/2006/07/23/cgssetwindowwarp-explained" rel="external">CGSSetWindowWarp</a>.</p>

<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/right.png" title="Warped clocks" alt="Warped using CGSSetWindowWarp" /><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/squish.png" title="Warped clocks" alt="Warped clocks" /><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/left.png" title="Warped clocks" alt="Warped clocks" /></p>

<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/control.png" title="Warped clocks" alt="Warped clocks" /><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/blur.png" title="Warped clocks" alt="Warped clocks" /></p>

<p>Did you find this useful? Or do you have any other method that you use for eye candy?</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Unleashing the Mac OS X Genie</title>
		<link>http://lipidity.com/apple/unleashing-the-mac-os-x-genie/</link>
		<comments>http://lipidity.com/apple/unleashing-the-mac-os-x-genie/#comments</comments>
		<pubDate>Wed, 22 Nov 2006 11:52:13 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Animation]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/apple/unleashing-the-mac-os-x-genie</guid>
		<description><![CDATA[How to use the genie effect in Cocoa with undocumented Core Graphics functions.]]></description>
			<content:encoded><![CDATA[<p><img src="http://dev.lipidity.com/wp-content/uploads/2006/11/genie.jpg" alt="Genie animation" /><br />
</p>

<p>
Look familiar? The Mac OS X genie effect is a <a rel="external" href="http://www.appleinsider.com/article.php?id=680" title="View information about the genie effect and Apple's patent on it.">patented</a> effect that is set as the default minimizing effect for windows in the Mac environment. Dating back to 2004, this animation involves a complex transformation of the source window in order to render it correctly on screen. Apple have not documented this feature for use in third party applications in Cocoa (<a href="https://developer.apple.com/documentation/Carbon/Reference/Window_Manager/Reference/reference.html#//apple_ref/c/func/TransitionWindow">It is available in Carbon</a>). It is something else that makes up the private portion of the <a href="http://dev.lipidity.com/apple/the-ultimate-core-graphics-resource" title="View my CGS framework which opens up a lot of the undocumented portions of Core Graphics.">Core Graphics framework</a>. However, keen developers like yourself can play with / use / abuse this awesome effect all you like.
</p>

<p>
The genie effect can be run using the private Core Graphics function <code>CGSCreateGenieWindowAnimation</code>. The requisites for this: A window to use the effect on, a connection to Core Graphics, and another window into which the source window goes. As an example, take a look at this code:</p>

<pre><code>- (void)doGenieFrom:(NSWindow*)source to:(NSWindow*)destination backwards:(BOOL)backwards {
    CGSAnimationRef ref;
    int err;

    err = CGSCreateGenieWindowAnimation(_CGSDefaultConnection(), [source windowNumber], [destination windowNumber], &#038;ref);

    if (backwards) {
        [source orderWindow:NSWindowBelow relativeTo:[destination windowNumber]]; /* No different to makeKeyAndOrderFront]*////
        [source setExcludedFromWindowsMenu:NO];
        [source flushWindow];
    }    
    
    if (0 == err) {
        float x;
        
        for (x = (backwards ? 1.0 : 0.0); (backwards ? (x &gt;= 0.0) : (x &lt;= 1.0)); (backwards ? (x -= 0.01) : (x += 0.01))) {            
            err = CGSSetWindowAnimationProgress(ref, x);
            
            usleep(1000);
        }
        
        err = CGSReleaseWindowAnimation(ref);
        
    }
    
    if (backwards) {
        [destination setExcludedFromWindowsMenu:YES];
    [source makeKeyAndOrderFront:self];
        [source setExcludedFromWindowsMenu:NO];
    } else {
        [source setExcludedFromWindowsMenu:YES];
        
        [destination makeKeyAndOrderFront:self];
        [destination setExcludedFromWindowsMenu:NO];
    }
}
</code></pre>

<p>How easy is that? Of course, this only compiles if you #import a header file that defines the Core Graphics private functions. (ie, just add <a href="http://dev.lipidity.com/wp-content/uploads/2006/11/coregraphicsservices.h">this file</a> to your Xcode project, then add a #import &#8220;coregraphicsservices.h&#8221; line at the top of the page where you want to use private CGS functions).</p>

<p><a class="imagelink" href="http://dev.lipidity.com/wp-content/uploads/2006/11/picture-1.png" title="Genie Effect via Framework" rel="lightbox"><img id="image67" src="http://dev.lipidity.com/wp-content/uploads/2006/11/picture-1.thumbnail.png" alt="Genie Effect via Framework" /></a></p>

<p>In the age of <a href="http://dev.lipidity.com/apple/passive-smokin">smoking windows</a>, <a href="http://dev.lipidity.com/apple/core-graphics-meet-core-image-demo-app">flashing views</a> and an unofficial <a href="http://dev.lipidity.com/apple/ilife-controls-hud-windows-and-more">redefinement of the <acronym title="Human Interface Guidelines">HIG</acronym></a> by developers, the genie animation can finally come out to play.</p>

<p>If you&#8217;re enthralled by the genie, here is a list of <a rel="external" href="http://www.silvermac.com/2006/minimising-windows-osx/">cool stuff you can do</a> on your mac regarding minimizing windows / expose.</p>

<p>Thoughts about this post? Add a comment, I value your feedback.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/unleashing-the-mac-os-x-genie/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Passive smokin&#8217;</title>
		<link>http://lipidity.com/apple/passive-smokin/</link>
		<comments>http://lipidity.com/apple/passive-smokin/#comments</comments>
		<pubDate>Wed, 01 Nov 2006 06:09:34 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Download]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[smoke]]></category>

		<category><![CDATA[Source Code]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/apple/passive-smokin</guid>
		<description><![CDATA[With all the fuss over Disco and it&#8217;s smoke effect, Jonathan Wight decided to take a deeper look. The result of his labor - anyone can integrate smoking windows into their apps. Stick around and let me show you how.



If you&#8217;re like me, one of the first things you do when you see a cool [...]]]></description>
			<content:encoded><![CDATA[<p>With all the fuss over <a href="http://discoapp.com" title="Disco is a CD burning shareware program for OS X created by the author of AppZapper and friends." rel="external">Disco</a> and it&#8217;s <a href="http://discoapp.com/blog/?p=23" title="The story behind the complex smoke effect as seen in Disco" rel="external">smoke effect</a>, Jonathan Wight decided to take a <a href="http://toxicsoftware.com/blog/second-hand-smoke/" rel="external" title="Using smoking windows in Xcode">deeper look</a>. The result of his labor - anyone can integrate smoking windows into their apps. Stick around and let me show you how.</p>

<p><a href="http://toxicsoftware.com/wordpress/uploads/SecondHandSmoke.png" title="Screenshot of a window smoking" class="imagelink" rel="lightbox"><img src="http://toxicsoftware.com/wordpress/uploads/SecondHandSmoke.png" alt="The smoke effect in action" /></a></p>

<p>If you&#8217;re like me, one of the first things you do when you see a cool app is dig straight into the resources folder. Well, right-clicking on Disco.app and hitting &#8220;Show package contents&#8221; is pretty revealing. Charge straight into the Contents folder, and you&#8217;ll see a nice little folder bearing the name &#8220;Frameworks&#8221;. Need I say any more? Just like all you need for Core Graphics is contained within my Core Graphics Framework, so the smoking windows code is contained in this little folder. It&#8217;s a simple matter of doing a class-dump to build some headers (or if you got in early, the headers are already there), and you have access to all the smoke you need. So, are you interested? Download the Disco beta and drag it to your applications folder. Then, you may proceed to grab a copy of my (modified) version of the smoking windows project and play around with it at your leisure! I quote from Toxic Software:</p>

<blockquote><p>If your hardware is capable enough you should be able to see the smoke effect rising from the top of the window. (It might take 10 to 20 seconds for the smoke to appear).

You can change the smoke parameters by using the smoke settings window (dialog? pane? view?) within Disco. Then just copy Disco’s prefs (com.discoapp.Disco.plist) to SecondHandSmoke’s prefs (com.example.SecondHandSmoke.plist).</p></blockquote>

<p>Obviously, this way of setting preferences isn&#8217;t very flash, so you can just set the specific preference keys from within your code, and it&#8217;ll still run fine. Don&#8217;t expect that done from me, though, I&#8217;ve got a lot of work to catch up on. Then again, if someone wants to make their window smoke and can&#8217;t get their head around the preferences, I can always take a little break and help you out.</p>

<p class="download"><a href="http://dev.lipidity.com/wp-content/uploads/2006/11/secondhandsmoke21.zip" title="Download the example project for smoking windows.">Second Hand Smoke</a></p>

<p>Have fun, and try not to get addicted to smoking.</p>

<p><ins class='block'><strong>Update:</strong> This smoke no longer works with the version 1.0 release of Disco. Updated smoking instructions <a href="http://dev.lipidity.com/feature/tutorial/now-this-is-hot-cocoa" title="Disco smoke with Cocoa and Xcode">here</a>.</ins></p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/passive-smokin/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
