Nick Sieger: Tag voodootag:blog.nicksieger.com,2005:TypoTypo2008-03-27T21:39:23+00:00Nick Siegerurn:uuid:3b535c66-18f9-4ae5-9e37-79757d8b7a322008-03-27T21:39:23+00:002008-03-27T21:39:23+00:00ImageVoodoo 0.1 Released<h2>Introducing ImageVoodoo</h2>
<p>I just pushed out the first release of ImageVoodoo, a nifty little image manipulation library conceived as a quick hack by <a href="http://www.bloglines.com/blog/ThomasEEnebo" title="Tom's Ruminations - Powered By Bloglines">Tom</a>. It’s a play-on-words of <a href="http://seattlerb.rubyforge.org/ImageScience.html" title="ImageScience">ImageScience</a>, of course, the quick, lightweight imaging library for Ruby. To get it,</p>
<pre><code>jruby -S gem install image_voodoo
</code></pre>
<p>What’s cool about ImageVoodoo (other than the name) is that we were able to make it API-compatible with ImageScience. In fact, ImageVoodoo’s <code>image_science.rb</code> simply looks like this:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="ident">require</span> <span class="punct">'</span><span class="string">image_voodoo</span><span class="punct">'</span>
<span class="comment"># HA HA...let the pin-pricking begin</span>
<span class="constant">ImageScience</span> <span class="punct">=</span> <span class="constant">ImageVoodoo</span></code></pre></div>
<p>So, you can use it pretty much anywhere you might use ImageScience, and it should <em>just work</em>. At work, we’re using it with <a href="http://github.com/technoweenie/attachment_fu/tree/master">attachment_fu</a>, and it works great. ImageVoodoo even steals and uses ImageScience’s unit tests (which all run successfully, too). Speed-wise, it’s about twice as slow as ImageScience running on MatzRuby, but still plenty fast enough for most cases.</p>
<p>But we wouldn’t be having fun unless we embraced and extended a little bit, right? So I added a couple of extra features you might find useful.</p>
<h2>Preview</h2>
<p>Since ImageVoodoo is just leveraging the Java Platform’s imaging libraries, image rendering can be easily tied into a simple preview frame. This code:</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="constant">ImageVoodoo</span><span class="punct">.</span><span class="ident">with_image</span><span class="punct">("</span><span class="string">samples/checkerboard.jpg</span><span class="punct">")</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">img</span><span class="punct">|</span>
<span class="ident">img</span><span class="punct">.</span><span class="ident">preview</span>
<span class="keyword">end</span></code></pre></div>
<p>Will pop up a little frame like this:</p>
<p><img src="/files/image_voodoo_preview.png" alt="preview" title="preview"/></p>
<p>The code that displays the preview frame is nice and compact, and shows off how nicely you can write clean swing GUI code using JRuby’s java integration features.</p>
<div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">ImageVoodoo</span>
<span class="keyword">class </span><span class="class">JImagePanel</span> <span class="punct"><</span> <span class="ident">javax</span><span class="punct">.</span><span class="ident">swing</span><span class="punct">.</span><span class="ident">JPanel</span>
<span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">image</span><span class="punct">,</span> <span class="ident">x</span><span class="punct">,</span> <span class="ident">y</span><span class="punct">)</span>
<span class="keyword">super</span><span class="punct">()</span>
<span class="attribute">@image</span><span class="punct">,</span> <span class="attribute">@x</span><span class="punct">,</span> <span class="attribute">@y</span> <span class="punct">=</span> <span class="ident">image</span><span class="punct">,</span> <span class="ident">x</span><span class="punct">,</span> <span class="ident">y</span>
<span class="keyword">end</span>
<span class="keyword">def </span><span class="method">paintComponent</span><span class="punct">(</span><span class="ident">graphics</span><span class="punct">)</span>
<span class="ident">graphics</span><span class="punct">.</span><span class="ident">drawImage</span><span class="punct">(</span><span class="attribute">@image</span><span class="punct">,</span> <span class="attribute">@x</span><span class="punct">,</span> <span class="attribute">@y</span><span class="punct">,</span> <span class="constant">nil</span><span class="punct">)</span>
<span class="keyword">end</span>
<span class="keyword">end</span>
<span class="keyword">class </span><span class="class">WindowClosed</span>
<span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">block</span> <span class="punct">=</span> <span class="constant">nil</span><span class="punct">)</span>
<span class="attribute">@block</span> <span class="punct">=</span> <span class="ident">block</span> <span class="punct">||</span> <span class="ident">proc</span> <span class="punct">{</span> <span class="ident">java</span><span class="punct">.</span><span class="ident">lang</span><span class="punct">.</span><span class="ident">System</span><span class="punct">.</span><span class="ident">exit</span><span class="punct">(</span><span class="number">0</span><span class="punct">)</span> <span class="punct">}</span>
<span class="keyword">end</span>
<span class="keyword">def </span><span class="method">method_missing</span><span class="punct">(</span><span class="ident">meth</span><span class="punct">,*</span><span class="ident">args</span><span class="punct">);</span> <span class="keyword">end</span>
<span class="keyword">def </span><span class="method">windowClosing</span><span class="punct">(</span><span class="ident">event</span><span class="punct">);</span> <span class="attribute">@block</span><span class="punct">.</span><span class="ident">call</span><span class="punct">;</span> <span class="keyword">end</span>
<span class="keyword">end</span>
<span class="keyword">def </span><span class="method">preview</span><span class="punct">(&</span><span class="ident">block</span><span class="punct">)</span>
<span class="ident">frame</span> <span class="punct">=</span> <span class="ident">javax</span><span class="punct">.</span><span class="ident">swing</span><span class="punct">.</span><span class="ident">JFrame</span><span class="punct">.</span><span class="ident">new</span><span class="punct">("</span><span class="string">Preview</span><span class="punct">")</span>
<span class="ident">frame</span><span class="punct">.</span><span class="ident">add_window_listener</span> <span class="constant">WindowClosed</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="ident">block</span><span class="punct">)</span>
<span class="ident">frame</span><span class="punct">.</span><span class="ident">set_bounds</span> <span class="number">0</span><span class="punct">,</span> <span class="number">0</span><span class="punct">,</span> <span class="ident">width</span> <span class="punct">+</span> <span class="number">20</span><span class="punct">,</span> <span class="ident">height</span> <span class="punct">+</span> <span class="number">40</span>
<span class="ident">frame</span><span class="punct">.</span><span class="ident">add</span> <span class="constant">JImagePanel</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(</span><span class="attribute">@src</span><span class="punct">,</span> <span class="number">10</span><span class="punct">,</span> <span class="number">10</span><span class="punct">)</span>
<span class="ident">frame</span><span class="punct">.</span><span class="ident">visible</span> <span class="punct">=</span> <span class="constant">true</span>
<span class="keyword">end</span>
<span class="keyword">end</span></code></pre></div>
<h2>Command-line</h2>
<p>As I was <a href="http://twitter.com/nicksieger/statuses/778203542">fixing a bug in ImageVoodoo’s file saving</a> I whipped up a little command-line utility to aid debugging. It allows you to string along several image manipulation actions on a single command-line. For example,</p>
<pre><code>jruby -S image_voodoo --push --resize 50x50 --preview --save t1.jpg \
--pop --resize 40x40 --preview --save t2.jpg \
--pop --resize 30x30 --preview --save t3.jpg image.jpg
</code></pre>
<p>This will resize <code>image.jpg</code> into three smaller images, <code>t[1-3].jpg</code>, but will pop up a preview frame at each step of the way. Simply close the preview frame to continue to the next action, or quit out of the application to abort.</p>
<h2>Summary</h2>
<p>And so, another functional area, image manipulation, becomes as easy on JRuby as it is on MatzRuby. Now that fancy social networking application you’ve been working on should have one less reason to be able to run unmodified on JRuby!</p>