{"id":4,"date":"2008-02-27T23:00:23","date_gmt":"2008-02-28T04:00:23","guid":{"rendered":"http:\/\/developer.casgrain.com\/?p=4"},"modified":"2008-09-12T22:31:11","modified_gmt":"2008-09-13T03:31:11","slug":"running-a-quartz-composition-in-your-application","status":"publish","type":"post","link":"http:\/\/developer.casgrain.com\/?p=4","title":{"rendered":"Running a Quartz composition in your application"},"content":{"rendered":"<h4>Updated Sept. 12th, 2008: make sure you don&#8217;t initWithOpenGLContext: a QCRenderer* outside of a @try&#8230;@catch block. <br \/>\nIf you do this on a 16 MB or less PCI video card, this will throw an exception instead of just returning a nil object.<br \/>\nThese video cards are not <a href=\"http:\/\/support.apple.com\/kb\/HT1582\">Quartz Extreme compatible<\/a>.<\/h4>\n<p>Now that you have created a <a href=\"http:\/\/developer.casgrain.com\/?p=3\">Quartz composition<\/a>, you are probably wondering if you can somehow reuse this prototype in your production code (C, C++ or Objective-C).<\/p>\n<p>For the purposes of this demo, it is assumed that you have an application written in C++ using Carbon. Cocoa applications can simplify this code as needed, for instance they may not need a local <tt>NSAutoreleasePool<\/tt>.<\/p>\n<h3>The Hard Way<\/h3>\n<p>Quartz Composer is a GUI on top of the Core Image filter library. Everything you see in QC represents one or more of the basic Core Image filters.<\/p>\n<p>\nTechnically, nothing prevents you from re-writing the composition by writing procedural code. Given an image <i>img<\/i>, you can:<\/p>\n<ul>\n<li>Load a CIFilter<\/li>\n<li>Set its parameters, including input image <i>img<\/i><\/li>\n<li>Apply the filter<\/li>\n<li>Get the new image <i>img2<\/i><\/li>\n<li>Unload the filter (if necessary)<\/li>\n<li>Repeat with <i>img2<\/i> and a new filter&#8230;<\/li>\n<\/ul>\n<p>This is tedious, error-prone and hard to maintain.<\/p>\n<p>You already did all the creative work in Quartz Composer, why not let Quartz Composer do the heavy lifting for you?<\/p>\n<h3>The easy way<\/h3>\n<p>When you think about it, our composition requires two pieces of data:<\/p>\n<ol>\n<li>A source image to operate on<\/li>\n<li>A place to store the resulting image<\/li>\n<\/ol>\n<p>If you were to treat a composition as a black box, the function prototype would probably look something like this:<\/p>\n<blockquote><p>\n  <code><br \/>\n  CGImageRef ApplyQuartzComposition(const char* compositionName, const CGImageRef srcImage);<br \/>\n  <\/code>\n<\/p><\/blockquote>\n<p>Pretty simple so far! Copy this in a header file (<tt>QuartzComposer.h<\/tt>, for instance) and add it to your application.<\/p>\n<h3>A small addition<\/h3>\n<p>As-is, our composition cannot be used. You will make some minor modifications to it to help run it from our application.<\/p>\n<p>First, you will add an intermediate, &#8220;do-nothing&#8221; image transformation. <\/p>\n<ul>\n<li>Disconnect your source image from the &#8220;Color Monochrome&#8221; and &#8220;Source Atop&#8221; filters, by dragging the tail end of the connexion away from the little dot labeled &#8220;Image&#8221;.\n<p><strong>The glowing image should disappear from the output window. This is expected.<\/strong><\/li>\n<li>Drag an &#8220;Image Transform&#8221; patch from the patch list into your composition<\/li>\n<li>Do not change the default parameters of this new patch. We simply want a &#8220;do-nothing&#8221; transformation.<\/li>\n<li>Connect the source image to the &#8220;Image&#8221; input (on the left) of the new Image Transform patch<\/li>\n<li>Connect the &#8220;Transformed Image&#8221; output of the new Image Transform patch to <i>both<\/i> the Color Monochrome, and Source Atop patches.\n<p><strong>The glowing image should re-appear in the output window.<\/strong><\/li>\n<\/ul>\n<p>When you are done, you should have something like this:<\/p>\n<p><div align=\"center\"><a href=\"http:\/\/www.labecasse.com\/philippe\/images\/glow-composition-new.png\" width=\"1055\" height=\"208\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.labecasse.com\/philippe\/images\/glow-composition-new-thumbnail.png\" width=\"527\" height=\"104\" \/><\/a><\/div>\n<\/p>\n<h3>Published outlets<\/h3>\n<p>The last thing to do is to to indicate in our composition where the source image is set, and where the resulting image can be copied.<\/p>\n<p>\nIf you control-click on any patch element of a composition, a contextual menu with the text &#8220;Published Inputs&#8221; and &#8220;Published Outputs&#8221; will appear. They will be disabled if there are no inputs or outputs. For instance, the source image you dragged in (on the left) has no inputs, and a Billboard has no outputs.<\/p>\n<p>\nUsing this technique, change the name of the published input &#8220;Image&#8221; of the Image Transform patch to &#8220;SourceImage&#8221;. The input&#8217;s name should now be &#8220;SourceImage&#8221; (with quotes), indicating that it is now a &#8220;named&#8221; input.<\/p>\n<blockquote><p>\n<em>But my picture disappeared!<\/em><\/p>\n<p>\nRight. As soon as you named the Input, the image was disconnected because a Quartz Composition cannot accept multiple inputs. It&#8217;s either a named input, or a connexion. <strong>This is expected.<\/strong>\n<\/p><\/blockquote>\n<p>Finally, name the output image of the &#8220;Source Atop&#8221; patch to &#8220;OutputImage&#8221;. Notice that the link to the Billboard was not severed, because outputs can be split.<\/p>\n<p>\n<em>Save your composition, with its named inputs and output, to a file called<\/em> <tt>Glow.qtz<\/tt>.<\/p>\n<h3>Adding the composition to your application as a resource<\/h3>\n<p>Your application is probably built in <a href=\"http:\/\/developer.apple.com\/tools\/xcode\/\">Xcode<\/a>, in which case you have a &#8220;Copy Resources&#8221; build phase. Simply add the Composition <tt>Glow.qtz<\/tt> to your project as a resource, and make sure it is added to this Copy phase. When you build your application, check the Contents\/Resources folder in your bundle: the composition should have been copied there.<\/p>\n<h3>Actual code<\/h3>\n<p>You declared a function called <code>ApplyQuartzComposition(const char*, const CGImageRef)<\/code> above. Here is the code to this function:<\/p>\n<blockquote>\n<pre>\n#import \"QuartzComposer.h\"\n#import &lt;Quartz\/Quartz.h&gt;\n\nCGImageRef ApplyQuartzComposition(const char* compositionName, CGImageRef srcImage)\n{\n  <strong>\/\/ Start with no image<\/strong>\n  CGImageRef resultImage = NULL;\n\n  <strong>\/\/ If you have a Cocoa application, you don't need an autorelease pool,\n  \/\/ but having an extra one does not hurt because pools can be nested.<\/strong>\n  NSAutoreleasePool* pool = [NSAutoreleasePool new];\n\n  <strong>\/\/ Load the Quartz Composition by name. You copied it to your app's Resources folder above.<\/strong>\n  NSString* compName = [NSString stringWithCString:compositionName];\n  NSString* compositionPath =  [[NSBundle mainBundle] pathForResource:compName ofType:@\"qtz\"];\n\n  <strong>\/\/ Quartz Compositions are run in an OpenGL context. Here's how to declare one.\n  \/\/ This code inspired by <a href=\"http:\/\/lists.apple.com\/archives\/Quartzcomposer-dev\/2005\/May\/\/msg00136.html\">http:\/\/lists.apple.com\/archives\/Quartzcomposer-dev\/2005\/May\/\/msg00136.html<\/a><\/strong>\n  NSOpenGLPixelFormatAttribute attributes[] = {NSOpenGLPFAAccelerated, NSOpenGLPFANoRecovery, (NSOpenGLPixelFormatAttribute)0};\n  NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];\n  NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil];\n\n  <strong>\/\/ We use Objective-C exceptions because if:\n  \/\/ - there is no context (which happens on a non-Quartz-Extreme graphics card), or\n  \/\/ - you forgot to name your inputs and outputs, or\n  \/\/ - you made a typo (hence they are not found),\n  \/\/ an exception is thrown and we want to catch it and return a NULL image.<\/strong>\n  @try\n  {\n    <strong>\/\/ Create the Renderer object that will play back our composition<\/strong>\n    QCRenderer* renderer = [[QCRenderer alloc] initWithOpenGLContext:context pixelFormat:format file:compositionPath];\n    <strong>\/\/ Set input values. You could get a complete list with [renderer inputKeys].<\/strong>\n    [renderer setValue:(id)srcImage forInputKey:@\"SourceImage\"];\n    <strong>\/\/ Run composition. Finally!<\/strong>\n    [renderer renderAtTime:0.0 arguments:nil];\n    <strong>\/\/ Retrieve composition output results. You could get a complete list of outputs with [renderer outputKeys].<\/strong>\n    NSImage* image = [renderer valueForOutputKey:@\"OutputImage\"];\n    if (image)\n    {\n      <strong>\/\/ Convert NSImage to CGImageRef, our return type<\/strong>\n      CFDataRef imgData = (CFDataRef)[image TIFFRepresentation];\n      CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData(imgData, NULL);\n      resultImage = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, NULL);\n    }\n    [renderer release];\n  }\n  @catch(id exception)\n  {\n    NSLog(@\"Error running Quartz Composition '%s': %@\", compositionName, exception);\n  }\n\n  <strong>\/\/ Done, clean up<\/strong>\n [context release];\n  [format release];\n  [pool release];\n  \n  return resultImage;\n}\n  <\/pre>\n<\/blockquote>\n<p>Copy this to a source file (<tt>QuartzComposer.mm<\/tt>, for instance) and add it to your application.<\/p>\n<p>\nYou should now be able to apply a Quartz Composition by name in your application. Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Updated Sept. 12th, 2008: make sure you don&#8217;t initWithOpenGLContext: a QCRenderer* outside of a @try&#8230;@catch block. If you do this on a 16 MB or less PCI video card, this will throw an exception instead of just returning a nil object. These video cards are not Quartz Extreme compatible. Now that you have created a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,1,3],"tags":[],"class_list":["post-4","post","type-post","status-publish","format-standard","hentry","category-carboncocoa","category-development","category-graphics"],"_links":{"self":[{"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts\/4","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4"}],"version-history":[{"count":0,"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=\/wp\/v2\/posts\/4\/revisions"}],"wp:attachment":[{"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/developer.casgrain.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}