UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

183 lines (175 loc) 30.2 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><title>Filtering Image Data</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Filtering Image Data"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-21-SECT-3"/>Filtering Image Data</h1></div></div></div><p>An <span class="emphasis"><em>image filter</em></span> is an object that performs transformations on image data. The Java 2D API supports image filtering through the <a id="I_indexterm21_id818300" class="indexterm"/><code class="literal">BufferedImageOp</code> interface. An image filter takes a <code class="literal">BufferedImage</code> as input (the <span class="emphasis"><em>source image</em></span>) and performs some processing on the image data, producing another <code class="literal">BufferedImage</code> (the <span class="emphasis"><em>destination image</em></span>).</p><p>The 2D API comes with a handy toolbox of <code class="literal">BufferedImageOp</code> implementations, as summarized in <a class="xref" href="ch21s03.html#learnjava3-CHP-21-TABLE-1" title="Table 21-1. Image operators in the 2D API">Table 21-1</a>.</p><div class="table"><a id="learnjava3-CHP-21-TABLE-1"/><p class="title">Table 21-1. Image operators in the 2D API</p><div class="table-contents"><table summary="Image operators in the 2D API" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col/><col/></colgroup><thead><tr><th style="text-align: left"><p>Name</p></th><th style="text-align: left"><p>Description</p></th></tr></thead><tbody><tr><td style="text-align: left"><p> <a id="I_indexterm21_id818387" class="indexterm"/> <code class="literal">AffineTransformOp</code> </p></td><td style="text-align: left"><p>Transforms an image geometrically</p></td></tr><tr><td style="text-align: left"><p> <a id="I_indexterm21_id818411" class="indexterm"/> <code class="literal">ColorConvertOp</code> </p></td><td style="text-align: left"><p>Converts from one color space to another</p></td></tr><tr><td style="text-align: left"><p> <a id="I_indexterm21_id818435" class="indexterm"/> <code class="literal">ConvolveOp</code> </p></td><td style="text-align: left"><p>Performs a convolution, a mathematical operation that can be used to blur, sharpen, or otherwise process an image</p></td></tr><tr><td style="text-align: left"><p> <a id="I_indexterm21_id818459" class="indexterm"/> <code class="literal">LookupOp</code> </p></td><td style="text-align: left"><p>Uses one or more lookup tables to process image values</p></td></tr><tr><td style="text-align: left"><p> <a id="I_indexterm21_id818483" class="indexterm"/> <code class="literal">RescaleOp</code> </p></td><td style="text-align: left"><p>Uses multiplication to process image values</p></td></tr></tbody></table></div></div><p>Let’s take a look at two of the simpler image operators. First, try the following application. It loads an image (the first command-line argument is the filename) and processes it in different ways as you select items from the combo box. The application is shown in <a class="xref" href="ch21s03.html#learnjava3-CHP-21-FIG-6" title="Figure 21-6. The ImageProcessor application">Figure 21-6</a>.</p><div class="figure"><a id="learnjava3-CHP-21-FIG-6"/><div class="figure-contents"><div class="mediaobject"><a id="I_21_tt1196"/><img src="httpatomoreillycomsourceoreillyimages1707703.png.jpg" alt="The ImageProcessor application"/></div></div><p class="title">Figure 21-6. The ImageProcessor application</p></div><p>Here’s the source code:</p><a id="I_21_tt1197"/><pre class="programlisting"> <code class="c1">//file: ImageProcessor.java</code> <code class="kn">import</code> <code class="nn">java.awt.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.awt.event.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.awt.geom.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.awt.image.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">ImageProcessor</code> <code class="kd">extends</code> <code class="n">JComponent</code> <code class="o">{</code> <code class="kd">private</code> <code class="n">BufferedImage</code> <code class="n">source</code><code class="o">,</code> <code class="n">destination</code><code class="o">;</code> <code class="kd">private</code> <code class="n">JComboBox</code> <code class="n">options</code><code class="o">;</code> <code class="kd">public</code> <code class="nf">ImageProcessor</code><code class="o">(</code> <code class="n">BufferedImage</code> <code class="n">image</code> <code class="o">)</code> <code class="o">{</code> <code class="n">source</code> <code class="o">=</code> <code class="n">destination</code> <code class="o">=</code> <code class="n">image</code><code class="o">;</code> <code class="n">setBackground</code><code class="o">(</code><code class="n">Color</code><code class="o">.</code><code class="na">white</code><code class="o">);</code> <code class="n">setLayout</code><code class="o">(</code><code class="k">new</code> <code class="n">BorderLayout</code><code class="o">());</code> <code class="c1">// create a panel to hold the combo box</code> <code class="n">JPanel</code> <code class="n">controls</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JPanel</code><code class="o">();</code> <code class="c1">// create the combo box with the names of the area operators</code> <code class="n">options</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JComboBox</code><code class="o">(</code> <code class="k">new</code> <code class="n">String</code><code class="o">[]</code> <code class="o">{</code> <code class="s">"[source]"</code><code class="o">,</code> <code class="s">"brighten"</code><code class="o">,</code> <code class="s">"darken"</code><code class="o">,</code> <code class="s">"rotate"</code><code class="o">,</code> <code class="s">"scale"</code> <code class="o">}</code> <code class="o">);</code> <code class="c1">// perform some processing when the selection changes</code> <code class="n">options</code><code class="o">.</code><code class="na">addItemListener</code><code class="o">(</code><code class="k">new</code> <code class="n">ItemListener</code><code class="o">()</code> <code class="o">{</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">itemStateChanged</code><code class="o">(</code><code class="n">ItemEvent</code> <code class="n">ie</code><code class="o">)</code> <code class="o">{</code> <code class="c1">// retrieve the selection option from the combo box</code> <code class="n">String</code> <code class="n">option</code> <code class="o">=</code> <code class="o">(</code><code class="n">String</code><code class="o">)</code><code class="n">options</code><code class="o">.</code><code class="na">getSelectedItem</code><code class="o">();</code> <code class="c1">// process the image according to the selected option</code> <code class="n">BufferedImageOp</code> <code class="n">op</code> <code class="o">=</code> <code class="kc">null</code><code class="o">;</code> <code class="k">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"[source]"</code><code class="o">))</code> <code class="n">destination</code> <code class="o">=</code> <code class="n">source</code><code class="o">;</code> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"brighten"</code><code class="o">))</code> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">RescaleOp</code><code class="o">(</code><code class="mf">1.5f</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"darken"</code><code class="o">))</code> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">RescaleOp</code><code class="o">(.</code><code class="mi">5</code><code class="n">f</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"rotate"</code><code class="o">))</code> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">AffineTransformOp</code><code class="o">(</code> <code class="n">AffineTransform</code><code class="o">.</code><code class="na">getRotateInstance</code><code class="o">(</code><code class="n">Math</code><code class="o">.</code><code class="na">PI</code> <code class="o">/</code> <code class="mi">6</code><code class="o">),</code> <code class="kc">null</code><code class="o">);</code> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"scale"</code><code class="o">))</code> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">AffineTransformOp</code><code class="o">(</code> <code class="n">AffineTransform</code><code class="o">.</code><code class="na">getScaleInstance</code><code class="o">(.</code><code class="mi">5</code><code class="o">,</code> <code class="o">.</code><code class="mi">5</code><code class="o">),</code> <code class="kc">null</code><code class="o">);</code> <code class="k">if</code> <code class="o">(</code><code class="n">op</code> <code class="o">!=</code> <code class="kc">null</code><code class="o">)</code> <code class="n">destination</code> <code class="o">=</code> <code class="n">op</code><code class="o">.</code><code class="na">filter</code><code class="o">(</code><code class="n">source</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="n">repaint</code><code class="o">();</code> <code class="o">}</code> <code class="o">});</code> <code class="n">controls</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="n">options</code><code class="o">);</code> <code class="n">add</code><code class="o">(</code><code class="n">controls</code><code class="o">,</code> <code class="n">BorderLayout</code><code class="o">.</code><code class="na">SOUTH</code><code class="o">);</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">paintComponent</code><code class="o">(</code><code class="n">Graphics</code> <code class="n">g</code><code class="o">)</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">imageWidth</code> <code class="o">=</code> <code class="n">destination</code><code class="o">.</code><code class="na">getWidth</code><code class="o">();</code> <code class="kt">int</code> <code class="n">imageHeight</code> <code class="o">=</code> <code class="n">destination</code><code class="o">.</code><code class="na">getHeight</code><code class="o">();</code> <code class="kt">int</code> <code class="n">width</code> <code class="o">=</code> <code class="n">getSize</code><code class="o">().</code><code class="na">width</code><code class="o">;</code> <code class="kt">int</code> <code class="n">height</code> <code class="o">=</code> <code class="n">getSize</code><code class="o">().</code><code class="na">height</code><code class="o">;</code> <code class="n">g</code><code class="o">.</code><code class="na">drawImage</code><code class="o">(</code><code class="n">destination</code><code class="o">,</code> <code class="o">(</code><code class="n">width</code> <code class="o">-</code> <code class="n">imageWidth</code><code class="o">)</code> <code class="o">/</code> <code class="mi">2</code><code class="o">,</code> <code class="o">(</code><code class="n">height</code> <code class="o">-</code> <code class="n">imageHeight</code><code class="o">)</code> <code class="o">/</code> <code class="mi">2</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="o">}</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code><code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code> <code class="n">String</code> <code class="n">filename</code> <code class="o">=</code> <code class="n">args</code><code class="o">[</code><code class="mi">0</code><code class="o">];</code> <code class="n">ImageIcon</code> <code class="n">icon</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ImageIcon</code><code class="o">(</code><code class="n">filename</code><code class="o">);</code> <code class="n">Image</code> <code class="n">i</code> <code class="o">=</code> <code class="n">icon</code><code class="o">.</code><code class="na">getImage</code><code class="o">();</code> <code class="c1">// draw the Image into a BufferedImage</code> <code class="kt">int</code> <code class="n">w</code> <code class="o">=</code> <code class="n">i</code><code class="o">.</code><code class="na">getWidth</code><code class="o">(</code><code class="kc">null</code><code class="o">),</code> <code class="n">h</code> <code class="o">=</code> <code class="n">i</code><code class="o">.</code><code class="na">getHeight</code><code class="o">(</code><code class="kc">null</code><code class="o">);</code> <code class="n">BufferedImage</code> <code class="n">buffImage</code> <code class="o">=</code> <code class="k">new</code> <code class="n">BufferedImage</code><code class="o">(</code><code class="n">w</code><code class="o">,</code> <code class="n">h</code><code class="o">,</code> <code class="n">BufferedImage</code><code class="o">.</code><code class="na">TYPE_INT_RGB</code><code class="o">);</code> <code class="n">Graphics2D</code> <code class="n">imageGraphics</code> <code class="o">=</code> <code class="n">buffImage</code><code class="o">.</code><code class="na">createGraphics</code><code class="o">();</code> <code class="n">imageGraphics</code><code class="o">.</code><code class="na">drawImage</code><code class="o">(</code><code class="n">i</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JFrame</code><code class="o">(</code><code class="s">"ImageProcessor"</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="k">new</code> <code class="n">ImageProcessor</code><code class="o">(</code><code class="n">buffImage</code><code class="o">));</code> <code class="n">frame</code><code class="o">.</code><code class="na">setSize</code><code class="o">(</code><code class="n">buffImage</code><code class="o">.</code><code class="na">getWidth</code><code class="o">(),</code> <code class="n">buffImage</code><code class="o">.</code><code class="na">getHeight</code><code class="o">());</code> <code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p>There’s quite a bit packed into the <code class="literal">ImageProcessor</code> application. After you’ve played around with it, come back and read about the details.</p><div class="sect2" title="How ImageProcessor Works"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-3.1"/>How ImageProcessor Works</h2></div></div></div><p><a id="I_indexterm21_id818584" class="indexterm"/> <a id="I_indexterm21_id818595" class="indexterm"/>The basic operation of <a id="I_indexterm21_id818606" class="indexterm"/><code class="literal">ImageProcessor</code> is very straightforward. It loads a source image, specified with a command-line argument in its <code class="literal">main()</code> method. The image is displayed along with a combo box. When you select different items from the combo box, <code class="literal">ImageProcessor</code> performs some image-processing operation on the source image and displays the result (the destination image). Most of this work occurs in the <code class="literal">ItemListener</code> event handler that is created in <code class="literal">ImageProcessor</code>’s constructor (a dubious design because we don’t want to tie up event-handling threads for too long, but we’ll let it slide here). Depending on the option that is selected, a <code class="literal">BufferedImageOp</code> (called <code class="literal">op</code>) is instantiated and used to process the source image, like this:</p><a id="I_21_tt1198"/><pre class="programlisting"> <code class="n">destination</code> <code class="o">=</code> <code class="n">op</code><code class="o">.</code><code class="na">filter</code><code class="o">(</code><code class="n">source</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code></pre><p>The destination image is returned from the <code class="literal">filter()</code> method. If we already had a destination image of the right size to hold the output, we could have passed it as the second argument to <code class="literal">filter()</code>, which would improve the performance of the application a bit. If you just pass <code class="literal">null</code>, as we have here, an appropriate destination image is created and returned to you. Once the destination image is created, <code class="literal">paint()</code>’s job is very simple; it just draws the destination image, centered on the component.</p></div><div class="sect2" title="Converting an Image to a BufferedImage"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-3.2"/>Converting an Image to a BufferedImage</h2></div></div></div><p><a id="I_indexterm21_id818698" class="indexterm"/> <a id="I_indexterm21_id818707" class="indexterm"/> <a id="I_indexterm21_id818717" class="indexterm"/>Image processing can be performed only on <code class="literal">BufferedImage</code>s, not <code class="literal">Image</code>s. Remember that the core AWT tools all work with <code class="literal">Image</code> and that only if you are loading images using the <code class="literal">ImageIO</code> package will you get <code class="literal">BufferedImage</code>s. Our <code class="literal">ImageProcessor</code> example demonstrates an important technique: how to convert a plain AWT <code class="literal">Image</code> to a <code class="literal">BufferedImage</code>. You do it by painting into the buffer, effectively copying the data. The <code class="literal">main()</code> method loads an <code class="literal">Image</code> from a file using <code class="literal">Toolkit</code>’s <code class="literal">getImage()</code> method:</p><a id="I_21_tt1199"/><pre class="programlisting"> <code class="n">Image</code> <code class="n">i</code> <code class="o">=</code> <code class="n">Toolkit</code><code class="o">.</code><code class="na">getDefaultToolkit</code><code class="o">().</code><code class="na">getImage</code><code class="o">(</code><code class="n">filename</code><code class="o">);</code></pre><p>Next, <code class="literal">main()</code> uses a <code class="literal">MediaTracker</code> to make sure the image data is fully loaded.</p><p>Finally, the trick of converting an <code class="literal">Image</code> to a <code class="literal">BufferedImage</code> is to draw the <code class="literal">Image</code> into the drawing surface of the <code class="literal">BufferedImage</code>. Because we know the <code class="literal">Image</code> is fully loaded, we just need to create a <code class="literal">BufferedImage</code>, get its graphics context, and draw the <code class="literal">Image</code> into it:</p><a id="I_21_tt1200"/><pre class="programlisting"> <code class="n">BufferedImage</code> <code class="n">bi</code> <code class="o">=</code> <code class="k">new</code> <code class="n">BufferedImage</code><code class="o">(</code><code class="n">w</code><code class="o">,</code> <code class="n">h</code><code class="o">,</code> <code class="n">BufferedImage</code><code class="o">.</code><code class="na">TYPE_INT_RGB</code><code class="o">);</code> <code class="n">Graphics2D</code> <code class="n">imageGraphics</code> <code class="o">=</code> <code class="n">bi</code><code class="o">.</code><code class="na">createGraphics</code><code class="o">();</code> <code class="n">imageGraphics</code><code class="o">.</code><code class="na">drawImage</code><code class="o">(</code><code class="n">i</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code></pre></div><div class="sect2" title="Using the RescaleOp Class"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-3.3"/>Using the RescaleOp Class</h2></div></div></div><p><a id="I_indexterm21_id818875" class="indexterm"/> <a id="I_indexterm21_id818885" class="indexterm"/> <a id="idx11138" class="indexterm"/> <span class="emphasis"><em>Rescaling</em></span> is an image operation that multiplies all the pixel values in the image by some constant. It doesn’t affect the size of the image in any way (in case you thought <span class="emphasis"><em>rescaling</em></span> meant <span class="emphasis"><em>scaling</em></span>), but it does affect the brightness of its pixel’s colors. In an RGB image, for example, each of the red, green, and blue values for each pixel would be multiplied by the rescaling multiplier. If you want, you can also adjust the results by adding an offset. In the 2D API, rescaling is performed by the <code class="literal">java.awt.image.RescaleOp</code> class. To create such an operator, specify the multiplier, offset, and a set of hints that control the quality of the conversion. In this case, we’ll use a zero offset and not bother with the hints (by passing <code class="literal">null</code>):</p><a id="I_21_tt1201"/><pre class="programlisting"> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">RescaleOp</code><code class="o">(</code><code class="mf">1.5f</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code></pre><p>Here, we’ve specified a multiplier of 1.5 and an offset of 0. All values in the destination image will be 1.5 times the values in the source image, which has the net result of making the image brighter. To perform the operation, we call the <code class="literal">filter()</code> method from the <code class="literal">BufferedImageOp</code> interface.<a id="I_indexterm21_id818966" class="indexterm"/></p></div><div class="sect2" title="Using the AffineTransformOp Class"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-3.4"/>Using the AffineTransformOp Class</h2></div></div></div><p><a id="idx11112" class="indexterm"/> <a id="idx11117" class="indexterm"/> <a id="idx11120" class="indexterm"/>An affine transformation is a kind of 2D transformation that preserves parallel lines; this includes operations like scaling, rotating, and shearing. The <code class="literal">java.awt.image.AffineTransformOp</code> image operator geometrically transforms a source image to produce the destination image. To create an <code class="literal">AffineTransformOp</code>, specify the transformation you want in the form of an <code class="literal">java.awt.geom.AffineTransform</code>. The <code class="literal">ImageProcessor</code> application includes two examples of this operator, one for rotation and one for scaling. As before, the <code class="literal">AffineTransformOp</code> constructor accepts a set of hints; we’ll just pass <code class="literal">null</code> to keep things simple:</p><a id="I_21_tt1202"/><pre class="programlisting"> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"rotate"</code><code class="o">))</code> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">AffineTransformOp</code><code class="o">(</code> <code class="n">AffineTransform</code><code class="o">.</code><code class="na">getRotateInstance</code><code class="o">(</code><code class="n">Math</code><code class="o">.</code><code class="na">PI</code> <code class="o">/</code> <code class="mi">6</code><code class="o">),</code> <code class="kc">null</code><code class="o">);</code> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">option</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">"scale"</code><code class="o">))</code> <code class="n">op</code> <code class="o">=</code> <code class="k">new</code> <code class="n">AffineTransformOp</code><code class="o">(</code> <code class="n">AffineTransform</code><code class="o">.</code><code class="na">getScaleInstance</code><code class="o">(.</code><code class="mi">5</code><code class="o">,</code> <code class="o">.</code><code class="mi">5</code><code class="o">),</code> <code class="kc">null</code><code class="o">);</code></pre><p>In both cases, we obtain an <code class="literal">AffineTransform</code> by calling one of its static methods. In the first case, we get a rotational transformation by supplying an angle. This transformation is wrapped in an <code class="literal">AffineTransformOp</code>. This operator has the effect of rotating the source image around its origin to create the destination image. In the second case, a scaling transformation is wrapped in an <code class="literal">AffineTransformOp</code>. The two scaling values, .5 and .5, specify that the image should be reduced to half its original size in both the x and y axes.</p><p>When using an <code class="literal">AffineTransformOp</code> to scale images, it’s important to note two things. Scaling an image up will always result in poor quality. When scaling an image down, and more generally with any affine transform, you can choose between speed and quality. Using <code class="literal">AffineTransformOp.TYPE_NEAREST_NEIGHBOR</code> as the second argument in your <code class="literal">AffineTransformOp</code> constructor will give you speed. For the best quality use <code class="literal">AffineTransformOp.TYPE_BICUBIC</code>. <code class="literal">AffineTransformOp.TYPE_BILINEAR</code> balances speed and quality.</p><p>One interesting aspect of <code class="literal">AffineTransformOp</code> is that you may “lose” part of your image when it’s transformed. For example, when using the rotate image operator in the <code class="literal">ImageProcessor</code> application, the destination image will have clipped some of the original image out. Both the source and destination images have the same origin, so if any part of the image gets transformed into negative x or y space, it is lost. To work around this problem, you can structure your transformations such that the entire destination image is in positive coordinate space.<a id="I_indexterm21_id819157" class="indexterm"/><a id="I_indexterm21_id819164" class="indexterm"/><a id="I_indexterm21_id819171" class="indexterm"/></p></div></div></body></html>