UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

333 lines (317 loc) 62.9 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>Producing 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="Producing Image Data"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-21-SECT-2"/>Producing Image Data</h1></div></div></div><p>There are two approaches to generating image data. The high-level method is to treat the image as a drawing surface and use the methods of <code class="literal">Graphics2D</code> to render things into the image. The second way is to twiddle the bits that represent the pixels of the image data yourself. This is harder, but gives you arbitrary control for handling specific formats or mathematically analyzing or creating image data.</p><div class="sect2" title="Drawing Animations"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-2.1"/>Drawing Animations</h2></div></div></div><p><a id="idx11116" class="indexterm"/> <a id="idx11129" class="indexterm"/>Let’s begin with the simpler approach, rendering an image through drawing. We’ll throw in a twist to make things interesting: we’ll build an animation. Each frame will be rendered as we go along. This is very similar to the double buffering we examined in the last chapter, except that this time we’ll use a timer instead of mouse events as the signal to generate new frames.</p><p>Swing performs double buffering automatically, so we don’t even need to worry about the animation flickering. Although it looks like we’re drawing directly to the screen, we’re really drawing into an image that Swing uses for double buffering. All we need to do is draw the right thing at the right time.</p><p>Let’s look at an example, <code class="literal">Hypnosis</code>, that illustrates the technique. This example shows a constantly shifting shape that bounces around the inside of a component. When screen savers first came of age, this kind of thing was pretty hot stuff. <code class="literal">Hypnosis</code> is shown in <a class="xref" href="ch21s02.html#learnjava3-CHP-21-FIG-2" title="Figure 21-2. A simple animation">Figure 21-2</a>.</p><div class="figure"><a id="learnjava3-CHP-21-FIG-2"/><div class="figure-contents"><div class="mediaobject"><a id="I_21_tt1183"/><img src="httpatomoreillycomsourceoreillyimages1707699.png.jpg" alt="A simple animation"/></div></div><p class="title">Figure 21-2. A simple animation</p></div><p>Here is its source code:</p><a id="I_21_tt1184"/><pre class="programlisting"> <code class="c1">//file: Hypnosis.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.GeneralPath</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">Hypnosis</code> <code class="kd">extends</code> <code class="n">JComponent</code> <code class="kd">implements</code> <code class="n">Runnable</code> <code class="o">{</code> <code class="kd">private</code> <code class="kt">int</code><code class="o">[]</code> <code class="n">coordinates</code><code class="o">;</code> <code class="kd">private</code> <code class="kt">int</code><code class="o">[]</code> <code class="n">deltas</code><code class="o">;</code> <code class="kd">private</code> <code class="n">Paint</code> <code class="n">paint</code><code class="o">;</code> <code class="kd">public</code> <code class="nf">Hypnosis</code><code class="o">(</code><code class="kt">int</code> <code class="n">numberOfSegments</code><code class="o">)</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">numberOfCoordinates</code> <code class="o">=</code> <code class="n">numberOfSegments</code> <code class="o">*</code> <code class="mi">4</code> <code class="o">+</code> <code class="mi">2</code><code class="o">;</code> <code class="n">coordinates</code> <code class="o">=</code> <code class="k">new</code> <code class="kt">int</code><code class="o">[</code><code class="n">numberOfCoordinates</code><code class="o">];</code> <code class="n">deltas</code> <code class="o">=</code> <code class="k">new</code> <code class="kt">int</code><code class="o">[</code><code class="n">numberOfCoordinates</code><code class="o">];</code> <code class="k">for</code> <code class="o">(</code><code class="kt">int</code> <code class="n">i</code> <code class="o">=</code> <code class="mi">0</code> <code class="o">;</code> <code class="n">i</code> <code class="o">&lt;</code> <code class="n">numberOfCoordinates</code><code class="o">;</code> <code class="n">i</code><code class="o">++)</code> <code class="o">{</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="o">(</code><code class="kt">int</code><code class="o">)(</code><code class="n">Math</code><code class="o">.</code><code class="na">random</code><code class="o">()</code> <code class="o">*</code> <code class="mi">300</code><code class="o">);</code> <code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="o">(</code><code class="kt">int</code><code class="o">)(</code><code class="n">Math</code><code class="o">.</code><code class="na">random</code><code class="o">()</code> <code class="o">*</code> <code class="mi">4</code> <code class="o">+</code> <code class="mi">3</code><code class="o">);</code> <code class="k">if</code> <code class="o">(</code><code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">&gt;</code> <code class="mi">4</code><code class="o">)</code> <code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="o">-(</code><code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">-</code> <code class="mi">3</code><code class="o">);</code> <code class="o">}</code> <code class="n">paint</code> <code class="o">=</code> <code class="k">new</code> <code class="n">GradientPaint</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="n">Color</code><code class="o">.</code><code class="na">BLUE</code><code class="o">,</code> <code class="mi">20</code><code class="o">,</code> <code class="mi">10</code><code class="o">,</code> <code class="n">Color</code><code class="o">.</code><code class="na">RED</code><code class="o">,</code> <code class="kc">true</code><code class="o">);</code> <code class="n">Thread</code> <code class="n">t</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Thread</code><code class="o">(</code><code class="k">this</code><code class="o">);</code> <code class="n">t</code><code class="o">.</code><code class="na">start</code><code class="o">();</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">run</code><code class="o">()</code> <code class="o">{</code> <code class="k">try</code> <code class="o">{</code> <code class="k">while</code> <code class="o">(</code><code class="kc">true</code><code class="o">)</code> <code class="o">{</code> <code class="n">timeStep</code><code class="o">();</code> <code class="n">repaint</code><code class="o">();</code> <code class="n">Thread</code><code class="o">.</code><code class="na">sleep</code><code class="o">(</code><code class="mi">1000</code> <code class="o">/</code> <code class="mi">24</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code> <code class="k">catch</code> <code class="o">(</code><code class="n">InterruptedException</code> <code class="n">ie</code><code class="o">)</code> <code class="o">{}</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">paint</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="n">Graphics2D</code> <code class="n">g2</code> <code class="o">=</code> <code class="o">(</code><code class="n">Graphics2D</code><code class="o">)</code><code class="n">g</code><code class="o">;</code> <code class="n">g2</code><code class="o">.</code><code class="na">setRenderingHint</code><code class="o">(</code><code class="n">RenderingHints</code><code class="o">.</code><code class="na">KEY_ANTIALIASING</code><code class="o">,</code> <code class="n">RenderingHints</code><code class="o">.</code><code class="na">VALUE_ANTIALIAS_ON</code><code class="o">);</code> <code class="n">Shape</code> <code class="n">s</code> <code class="o">=</code> <code class="n">createShape</code><code class="o">();</code> <code class="n">g2</code><code class="o">.</code><code class="na">setPaint</code><code class="o">(</code><code class="n">paint</code><code class="o">);</code> <code class="n">g2</code><code class="o">.</code><code class="na">fill</code><code class="o">(</code><code class="n">s</code><code class="o">);</code> <code class="n">g2</code><code class="o">.</code><code class="na">setPaint</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">g2</code><code class="o">.</code><code class="na">draw</code><code class="o">(</code><code class="n">s</code><code class="o">);</code> <code class="o">}</code> <code class="kd">private</code> <code class="kt">void</code> <code class="nf">timeStep</code><code class="o">()</code> <code class="o">{</code> <code class="n">Dimension</code> <code class="n">d</code> <code class="o">=</code> <code class="n">getSize</code><code class="o">();</code> <code class="k">if</code> <code class="o">(</code><code class="n">d</code><code class="o">.</code><code class="na">width</code> <code class="o">==</code> <code class="mi">0</code> <code class="o">||</code> <code class="n">d</code><code class="o">.</code><code class="na">height</code> <code class="o">==</code> <code class="mi">0</code><code class="o">)</code> <code class="k">return</code><code class="o">;</code> <code class="k">for</code> <code class="o">(</code><code class="kt">int</code> <code class="n">i</code> <code class="o">=</code> <code class="mi">0</code><code class="o">;</code> <code class="n">i</code> <code class="o">&lt;</code> <code class="n">coordinates</code><code class="o">.</code><code class="na">length</code><code class="o">;</code> <code class="n">i</code><code class="o">++)</code> <code class="o">{</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">+=</code> <code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">];</code> <code class="kt">int</code> <code class="n">limit</code> <code class="o">=</code> <code class="o">(</code><code class="n">i</code> <code class="o">%</code> <code class="mi">2</code> <code class="o">==</code> <code class="mi">0</code><code class="o">)</code> <code class="o">?</code> <code class="n">d</code><code class="o">.</code><code class="na">width</code> <code class="o">:</code> <code class="n">d</code><code class="o">.</code><code class="na">height</code><code class="o">;</code> <code class="k">if</code> <code class="o">(</code><code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">&lt;</code> <code class="mi">0</code><code class="o">)</code> <code class="o">{</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="mi">0</code><code class="o">;</code> <code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="o">-</code><code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">];</code> <code class="o">}</code> <code class="k">else</code> <code class="nf">if</code> <code class="o">(</code><code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">&gt;</code> <code class="n">limit</code><code class="o">)</code> <code class="o">{</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="n">limit</code> <code class="o">-</code> <code class="mi">1</code><code class="o">;</code> <code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">]</code> <code class="o">=</code> <code class="o">-</code><code class="n">deltas</code><code class="o">[</code><code class="n">i</code><code class="o">];</code> <code class="o">}</code> <code class="o">}</code> <code class="o">}</code> <code class="kd">private</code> <code class="n">Shape</code> <code class="nf">createShape</code><code class="o">()</code> <code class="o">{</code> <code class="n">GeneralPath</code> <code class="n">path</code> <code class="o">=</code> <code class="k">new</code> <code class="n">GeneralPath</code><code class="o">();</code> <code class="n">path</code><code class="o">.</code><code class="na">moveTo</code><code class="o">(</code><code class="n">coordinates</code><code class="o">[</code><code class="mi">0</code><code class="o">],</code> <code class="n">coordinates</code><code class="o">[</code><code class="mi">1</code><code class="o">]);</code> <code class="k">for</code> <code class="o">(</code><code class="kt">int</code> <code class="n">i</code> <code class="o">=</code> <code class="mi">2</code><code class="o">;</code> <code class="n">i</code> <code class="o">&lt;</code> <code class="n">coordinates</code><code class="o">.</code><code class="na">length</code><code class="o">;</code> <code class="n">i</code> <code class="o">+=</code> <code class="mi">4</code><code class="o">)</code> <code class="n">path</code><code class="o">.</code><code class="na">quadTo</code><code class="o">(</code><code class="n">coordinates</code><code class="o">[</code><code class="n">i</code><code class="o">],</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code> <code class="o">+</code> <code class="mi">1</code><code class="o">],</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code> <code class="o">+</code> <code class="mi">2</code><code class="o">],</code> <code class="n">coordinates</code><code class="o">[</code><code class="n">i</code> <code class="o">+</code> <code class="mi">3</code><code class="o">]);</code> <code class="n">path</code><code class="o">.</code><code class="na">closePath</code><code class="o">();</code> <code class="k">return</code> <code class="n">path</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">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">"Hypnosis"</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">Hypnosis</code><code class="o">(</code><code class="mi">4</code><code class="o">)</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="mi">300</code><code class="o">,</code> <code class="mi">300</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>The <code class="literal">main()</code> method does the usual grunt work of setting up the <code class="literal">JFrame</code> that holds our animation component.</p><p>The <code class="literal">Hypnosis</code> component has a very basic strategy for animation. It holds some number of coordinate pairs in its coordinates member variable. A corresponding array, <code class="literal">deltas</code>, holds “delta” amounts that are added to the coordinates every time the figure is supposed to change. To render the complex shape you see in <a class="xref" href="ch21s02.html#learnjava3-CHP-21-FIG-2" title="Figure 21-2. A simple animation">Figure 21-2</a>, <code class="literal">Hypnosis</code> creates a special <code class="literal">Shape</code> object from the coordinate array every time the component is drawn.</p><p><code class="literal">Hypnosis</code>’s constructor has two important tasks. First, it fills up the coordinate and delta arrays with random values. The number of array elements is determined by an argument to the constructor. The constructor’s second task is to start up a new thread that drives the animation.</p><p>The animation is done in the <code class="literal">run()</code> method. This method calls <code class="literal">timeStep()</code>, which repaints the component and waits for a short time (details to follow). Every time <code class="literal">timeStep()</code> is called, the coordinates array is updated and <code class="literal">repaint()</code> is called. This results in a call to <code class="literal">paint()</code>, which creates a shape from the coordinate array and draws it.</p><p>The <code class="literal">paint()</code> method is relatively simple. It uses a helper method, called <code class="literal">createShape()</code>, to create a shape from the coordinate array. The shape is then filled, using a <code class="literal">Paint</code> stored as a member variable. The shape’s outline is also drawn in white.</p><p>The <code class="literal">timeStep()</code> method updates all the elements of the coordinate array by adding the corresponding element of deltas. If any coordinates are now out of the component’s bounds, they are adjusted and the corresponding delta is negated. This produces the effect of bouncing off the sides of the component.</p><p><code class="literal">createShape()</code> creates a shape from the coordinate array. It uses the <code class="literal">GeneralPath</code> class, a useful <code class="literal">Shape</code> implementation that allows you to build shapes using straight and curved line segments. In this case, we create a shape from a series of quadratic curves, close it to create an area, and fill it.<a id="I_indexterm21_id817015" class="indexterm"/><a id="I_indexterm21_id817022" class="indexterm"/></p></div><div class="sect2" title="BufferedImage Anatomy"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-2.2"/>BufferedImage Anatomy</h2></div></div></div><p><a id="idx11113" class="indexterm"/> <a id="idx11125" class="indexterm"/>So far, we’ve talked about <code class="literal">java.awt.Image</code>s and how they can be loaded and drawn. What if you really want to get inside the image to examine and update its data? <code class="literal">Image</code> doesn’t give you access to its data. You’ll need to use a more sophisticated kind of image: <code class="literal">java.awt.image.BufferedImage</code>. The classes are closely related—<code class="literal">BufferedImage</code>, in fact, is a subclass of <code class="literal">Image</code>. <code class="literal">BufferedImage</code> gives you all sorts of control over the actual data that makes up the image and provides many capabilities beyond the basic <code class="literal">Image</code> class. Because it’s a subclass of <code class="literal">Image</code>, you can still pass a <code class="literal">BufferedImage</code> to any of <code class="literal">Graphics2D</code>’s methods that accept an <code class="literal">Image</code>. Why aren’t all <code class="literal">Image</code>s <code class="literal">BufferedImage</code>s? Because <code class="literal">BufferedImage</code>s are memory intensive.</p><p>To create an image from raw data, you need to understand exactly how a <code class="literal">BufferedImage</code> is put together. The full details can get quite complex—the <code class="literal">BufferedImage</code> class was designed to support images in nearly any storage format you can imagine. But, for common operations, it’s not that difficult to use. <a class="xref" href="ch21s02.html#learnjava3-CHP-21-FIG-3" title="Figure 21-3. Inside a BufferedImage">Figure 21-3</a> shows the elements of a <code class="literal">BufferedImage</code>.</p><div class="figure"><a id="learnjava3-CHP-21-FIG-3"/><div class="figure-contents"><div class="mediaobject"><a id="I_21_tt1185"/><img src="httpatomoreillycomsourceoreillyimages1707700.png" alt="Inside a BufferedImage"/></div></div><p class="title">Figure 21-3. Inside a BufferedImage</p></div><p>An image is simply a rectangle of colored pixels, which is a simple enough concept. There’s a lot of complexity underneath the <code class="literal">BufferedImage</code> class because there are a lot of different ways to represent the colors of pixels. For example, you might have an image with RGB data in which each pixel’s red, green, and blue values were stored as the elements of byte arrays. Or you might have an RGB image where each pixel was represented by an integer that contained red, green, and blue component values. Or you could have a 16-level grayscale image with eight pixels stored in each element of an integer array. You get the idea; there are many different ways to store image data, and <code class="literal">BufferedImage</code> is designed to support all of them.</p><p>A <code class="literal">BufferedImage</code> consists of two pieces, a <a id="I_indexterm21_id817223" class="indexterm"/><code class="literal">Raster</code> and a <a id="I_indexterm21_id817234" class="indexterm"/><code class="literal">ColorModel</code>. The <code class="literal">Raster</code> contains the actual image data. You can think of it as an array of pixel values. It can answer the question, “What are the color data values for the pixel at 51, 17?” The <code class="literal">Raster</code> for an RGB image would return three values, while a <code class="literal">Raster</code> for a grayscale image would return a single value. <a id="I_indexterm21_id817264" class="indexterm"/><code class="literal">WritableRaster</code>, a subclass of <code class="literal">Raster</code>, also supports modifying pixel data values.</p><p>The <code class="literal">ColorModel</code>’s job is to interpret the image data as colors. The <code class="literal">ColorModel</code> can translate the data values that come from the <code class="literal">Raster</code> into <code class="literal">Color</code> objects. An RGB color model, for example, would know how to interpret three data values as red, green, and blue. A grayscale color model could interpret a single data value as a gray level. Conceptually, at least, this is how an image is displayed on the screen. The graphics system retrieves the data for each pixel of the image from the <code class="literal">Raster</code>. Then the <code class="literal">ColorModel</code> tells what color each pixel should be, and the graphics system is able to set the color of each pixel.</p><p>The <code class="literal">Raster</code> itself is made up of two pieces: a <code class="literal">DataBuffer</code> and a <code class="literal">SampleModel</code>. A <code class="literal">DataBuffer</code> is a wrapper for the raw data arrays, which are <code class="literal">byte</code>, <code class="literal">short</code>, or <code class="literal">int</code> arrays. <code class="literal">DataBuffer</code> has handy subclasses—<code class="literal">DataBufferByte</code>, <code class="literal">DataBufferShort</code>, and <code class="literal">DataBufferInt</code>—that allow you to create a <code class="literal">DataBuffer</code> from raw data arrays. You’ll see an example of this technique later in the <code class="literal">StaticGenerator</code> example.</p><p>The <code class="literal">SampleModel</code> knows how to extract the data values for a particular pixel from the <code class="literal">DataBuffer</code>. It knows the layout of the arrays in the <code class="literal">DataBuffer</code> and is ultimately responsible for answering the question, “What are the data values for pixel x, y?” <code class="literal">SampleModel</code>s are a little tricky to work with, but fortunately you’ll probably never need to create or use one directly. As we’ll see, the <code class="literal">Raster</code> class has many static (“factory”) methods that create preconfigured <code class="literal">Raster</code>s for you, including their component <code class="literal">DataBuffer</code>s and <code class="literal">SampleModel</code>s.</p><p>As <a class="xref" href="ch21.html#learnjava3-CHP-21-FIG-1" title="Figure 21-1. The java.awt.image package">Figure 21-1</a> shows, the 2D API comes with various flavors of <code class="literal">ColorModel</code>s, <code class="literal">SampleModel</code>s, and <code class="literal">DataBuffer</code>s. These serve as handy building blocks that cover most common image storage formats. You’ll rarely need to subclass any of these classes to create a <code class="literal">BufferedImage</code>.<a id="I_indexterm21_id817476" class="indexterm"/><a id="I_indexterm21_id817483" class="indexterm"/></p></div><div class="sect2" title="Color Models"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-2.3"/>Color Models</h2></div></div></div><p><a id="idx11115" class="indexterm"/> <a id="idx11127" class="indexterm"/>As we’ve said, there are many different ways to encode color information: red, green, blue (RGB) values; hue, saturation, value (HSV); hue, lightness, saturation (HLS); and more. In addition, you can provide full-color information for each pixel, or you can just specify an index into a color table (palette) for each pixel. The way you represent a color is called a <span class="emphasis"><em>color model</em></span>. The 2D API provides tools to support any color model you could imagine. Here, we’ll just cover two broad groups of color models: <a id="I_indexterm21_id817533" class="indexterm"/><span class="emphasis"><em>direct</em></span> and <a id="I_indexterm21_id817542" class="indexterm"/><span class="emphasis"><em>indexed</em></span>.</p><p>As you might expect, you must specify a color model in order to generate pixel data; the <code class="literal">abstract</code> class <a id="I_indexterm21_id817559" class="indexterm"/><code class="literal">java.awt.image.ColorModel</code> represents a color model. By default, Java 2D uses a direct color model called ARGB. The A stands for “alpha,” which is the historical name for transparency. RGB refers to the red, green, and blue color components that are combined to produce a single, composite color. In the default ARGB model, each pixel is represented by a 32-bit integer that is interpreted as four 8-bit fields; in order, the fields represent the alpha (transparency), red, green, and blue components of the color, as shown in <a class="xref" href="ch21s02.html#learnjava3-CHP-21-FIG-4" title="Figure 21-4. ARGB color encoding">Figure 21-4</a>.</p><div class="figure"><a id="learnjava3-CHP-21-FIG-4"/><div class="figure-contents"><div class="mediaobject"><a id="I_21_tt1186"/><img src="httpatomoreillycomsourceoreillyimages1707701.png" alt="ARGB color encoding"/></div></div><p class="title">Figure 21-4. ARGB color encoding</p></div><p>To create an instance of the default ARGB model, call the <code class="literal">static getRGBdefault()</code> method in <code class="literal">ColorModel</code>. This method returns a <code class="literal">DirectColorModel</code> object; <code class="literal">DirectColorModel</code> is a subclass of <code class="literal">ColorModel</code>. You can also create other direct color models by calling a <code class="literal">DirectColorModel</code> constructor, but you shouldn’t need to do this unless you have a fairly exotic application.</p><p>In an indexed color model, each pixel is represented by a smaller piece of information: an index into a table of real color values. Several common image formats, including GIF, use an indexed color model. For some applications, generating data with an indexed model may be more convenient. If you are writing an application for an 8-bit display or smaller, using an indexed model may be more efficient, because your hardware is internally using an indexed color model of some form.<a id="I_indexterm21_id817645" class="indexterm"/><a id="I_indexterm21_id817652" class="indexterm"/></p></div><div class="sect2" title="Creating an Image"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-2.4"/>Creating an Image</h2></div></div></div><p><a id="idx11128" class="indexterm"/>Let’s take a look at producing some image data. A picture is worth a thousand words, and, fortunately, we can generate a pretty picture in significantly fewer than a thousand words of Java. If we just want to render image frames byte by byte, you can put together a <code class="literal">BufferedImage</code> pretty easily.</p><p>The following application, <code class="literal">ColorPan</code>, creates an image from an array of integers holding RGB pixel values:</p><a id="I_21_tt1187"/><pre class="programlisting"> <code class="c1">//file: ColorPan.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.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">ColorPan</code> <code class="kd">extends</code> <code class="n">JComponent</code> <code class="o">{</code> <code class="n">BufferedImage</code> <code class="n">image</code><code class="o">;</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">initialize</code><code class="o">()</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="kt">int</code><code class="o">[]</code> <code class="n">data</code> <code class="o">=</code> <code class="k">new</code> <code class="kt">int</code> <code class="o">[</code><code class="n">width</code> <code class="o">*</code> <code class="n">height</code><code class="o">];</code> <code class="kt">int</code> <code class="n">i</code> <code class="o">=</code> <code class="mi">0</code><code class="o">;</code> <code class="k">for</code> <code class="o">(</code><code class="kt">int</code> <code class="n">y</code> <code class="o">=</code> <code class="mi">0</code><code class="o">;</code> <code class="n">y</code> <code class="o">&lt;</code> <code class="n">height</code><code class="o">;</code> <code class="n">y</code><code class="o">++)</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">red</code> <code class="o">=</code> <code class="o">(</code><code class="n">y</code> <code class="o">*</code> <code class="mi">255</code><code class="o">)</code> <code class="o">/</code> <code class="o">(</code><code class="n">height</code> <code class="o">-</code> <code class="mi">1</code><code class="o">);</code> <code class="k">for</code> <code class="o">(</code><code class="kt">int</code> <code class="n">x</code> <code class="o">=</code> <code class="mi">0</code><code class="o">;</code> <code class="n">x</code> <code class="o">&lt;</code> <code class="n">width</code><code class="o">;</code> <code class="n">x</code><code class="o">++)</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">green</code> <code class="o">=</code> <code class="o">(</code><code class="n">x</code> <code class="o">*</code> <code class="mi">255</code><code class="o">)</code> <code class="o">/</code> <code class="o">(</code><code class="n">width</code> <code class="o">-</code> <code class="mi">1</code><code class="o">);</code> <code class="kt">int</code> <code class="n">blue</code> <code class="o">=</code> <code class="mi">128</code><code class="o">;</code> <code class="n">data</code><code class="o">[</code><code class="n">i</code><code class="o">++]</code> <code class="o">=</code> <code class="o">(</code><code class="n">red</code> <code class="o">&lt;&lt;</code> <code class="mi">16</code><code class="o">)</code> <code class="o">|</code> <code class="o">(</code><code class="n">green</code> <code class="o">&lt;&lt;</code> <code class="mi">8</code> <code class="o">)</code> <code class="o">|</code> <code class="n">blue</code><code class="o">;</code> <code class="o">}</code> <code class="o">}</code> <code class="n">image</code> <code class="o">=</code> <code class="k">new</code> <code class="n">BufferedImage</code><code class="o">(</code><code class="n">width</code><code class="o">,</code> <code class="n">height</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">image</code><code class="o">.</code><code class="na">setRGB</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="n">width</code><code class="o">,</code> <code class="n">height</code><code class="o">,</code> <code class="n">data</code><code class="o">,</code> <code class="mi">0</code><code class="o">,</code> <code class="n">width</code><code class="o">);</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">paint</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="k">if</code> <code class="o">(</code><code class="n">image</code> <code class="o">==</code> <code class="kc">null</code><code class="o">)</code> <code class="n">initialize</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">image</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="k">this</code><code class="o">);</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">setBounds</code><code class="o">(</code><code class="kt">int</code> <code class="n">x</code><code class="o">,</code> <code class="kt">int</code> <code class="n">y</code><code class="o">,</code> <code class="kt">int</code> <code class="n">width</code><code class="o">,</code> <code class="kt">int</code> <code class="n">height</code><code class="o">)</code> <code class="o">{</code> <code class="kd">super</code><code class="o">.</code><code class="na">setBounds</code><code class="o">(</code><code class="n">x</code><code class="o">,</code><code class="n">y</code><code class="o">,</code><code class="n">width</code><code class="o">,</code><code class="n">height</code><code class="o">);</code> <code class="n">initialize</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">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">"ColorPan"</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">ColorPan</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="mi">300</code><code class="o">,</code> <code class="mi">300</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>Give it a try. The size of the image is determined by the size of the application window. You should get a very colorful box that pans from deep blue at the upper-left corner to bright yellow at the bottom right, with green and red at the other extremes.</p><p>We create a <code class="literal">BufferedImage</code> in the <a id="I_indexterm21_id817747" class="indexterm"/><code class="literal">initialize()</code> method and then display the image in <code class="literal">paint()</code>. The variable <code class="literal">data</code> is a 1D array of integers that holds 32-bit RGB pixel values. In <code class="literal">initialize()</code>, we loop over every pixel in the image and assign it an RGB value. The blue component is always 128, half its maximum intensity. The red component varies from 0 to 255 along the y-axis; likewise, the green component varies from 0 to 255 along the x-axis. This statement combines these components into an RGB value:</p><a id="I_21_tt1188"/><pre class="programlisting"> <code class="n">data</code><code class="o">[</code><code class="n">i</code><code class="o">++]</code> <code class="o">=</code> <code class="o">(</code><code class="n">red</code> <code class="o">&lt;&lt;</code> <code class="mi">16</code><code class="o">)</code> <code class="o">|</code> <code class="o">(</code><code class="n">green</code> <code class="o">&lt;&lt;</code> <code class="mi">8</code> <code class="o">)</code> <code class="o">|</code> <code class="n">blue</code><code class="o">;</code></pre><p>The bitwise left-shift operator (<code class="literal">&lt;&lt;</code>) should be familiar to anyone who has programmed in C. It simply shoves the bits over by the specified number of positions in our 32-bit value.</p><p>When we create the <code class="literal">BufferedImage</code>, all its data is zeroed out. All we specify in the constructor is the width and height of the image and its type. <code class="literal">BufferedImage</code> includes quite a few constants representing image storage types. We’ve chosen <a id="I_indexterm21_id817812" class="indexterm"/><code class="literal">TYPE_INT_RGB</code> here, which indicates that we want to store the image as RGB data packed into integers. The constructor takes care of creating an appropriate <code class="literal">ColorModel</code>, <code class="literal">Raster</code>, <code class="literal">SampleModel</code>, and <code class="literal">DataBuffer</code> for us. Then we simply use the <a id="I_indexterm21_id817845" class="indexterm"/><code class="literal">setRGB()</code> method to assign our data to the image. In this way, we’ve side-stepped the messy innards of <code class="literal">BufferedImage</code>. In the next example, we’ll take a closer look at the details.</p><p>Once we have the image, we can draw it on the display with the standard <code class="literal">drawImage()</code> method. We also override the <code class="literal">Component setBounds()</code> method in order to determine when the frame is resized and reinitialize the image to the new size.<a id="I_indexterm21_id817879" class="indexterm"/></p></div><div class="sect2" title="Updating a BufferedImage"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-21-SECT-2.5"/>Updating a BufferedImage</h2></div></div></div><p><a id="idx11114" class="indexterm"/> <a id="idx11126" class="indexterm"/> <code class="literal">BufferedImage</code> can also be used to update an image dynamically. Because the image’s data arrays are directly accessible, you can simply change the data and redraw the picture whenever you want. This is probably the easiest way to build your own low-level animation software. The following example simulates the static on an old black-and-white television screen. It generates successive frames of random black and white pixels and displays each frame when it is complete. <a class="xref" href="ch21s02.html#learnjava3-CHP-21-FIG-5" title="Figure 21-5. A frame of random static">Figure 21-5</a> shows one frame of random static.</p><div class="figure"><a id="learnjava3-CHP-21-FIG-5"/><div class="figure-contents"><div class="mediaobject"><a id="I_21_tt1189"/><img src="httpatomoreillycomsourceoreillyimages1707702.png" alt="A frame of random static"/></div></div><p class="title">Figure 21-5. A frame of random static</p></div><p>Here’s the code:</p><a id="I_21_tt1190"/><pre class="programlisting"> <code class="c1">//file: StaticGenerator.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.image.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.util.Random</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">StaticGenerator</code> <code class="kd">extends</code> <code class="n">JComponent</code> <code class="kd">implements</code> <code class="n">Runnable</code> <code class="o">{</code> <code class="kt">byte</code><code class="o">[]</code> <code class="n">data</code><code class="o">;</code> <code class="n">BufferedImage</code> <code class="n">image</code><code class="o">;</code> <code class="n">Random</code> <code class="n">random</code><code class="o">;</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">initialize</code><code class="o">()</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">w</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="n">h</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="kt">int</code> <code class="n">length</code> <code class="o">=</code> <code class="o">((</code><code class="n">w</code> <code class="o">+</code> <code class="mi">7</code><code class="o">)</code> <code class="o">*</code> <code class="n">h</code><code class="o">)</code> <code class="o">/</code> <code class="mi">8</code><code class="o">;</code> <code class="n">data</code> <code class="o">=</code> <code class="k">new</code> <code class="kt">byte</code><code class="o">[</code><code class="n">length</code><code class="o">];</code> <code class="n">DataBuffer</code> <code class="n">db</code> <code class="o">=</code> <code class="k">new</code> <code class="n">DataBufferByte</code><code class="o">(</code><code class="n">data</code><code class="o">,</code> <code class="n">length</code><code class="o">);</code> <code class="n">WritableRaster</code> <code class="n">wr</code> <code class="o">=</code> <code class="n">Raster</code><code class="o">.</code><code class="na">createPackedRaster</code><code class="o">(</code><code class="n">db</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="mi">1</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="n">ColorModel</code> <code class="n">cm</code> <code class="o">=</code> <code class="k">new</code> <code class="n">IndexColorModel</code><code class="o">(</code><code class="mi">1</code><code class="o">,</code> <code class="mi">2</code><code class="o">,</code> <code class="k">new</code> <code class="kt">byte</code><code class="o">[]</code> <code class="o">{</code> <code class="o">(</code><code class="kt">byte</code><code class="o">)</code><code class="mi">0</code><code class="o">,</code> <code class="o">(</code><code class="kt">byte</code><code class="o">)</code><code class="mi">255</code> <code class="o">},</code> <code class="k">new</code> <code class="kt">byte</code><code class="o">[]</code> <code class="o">{</code> <code class="o">(</code><code class="kt">byte</code><code class="o">)</code><code class="mi">0</code><code class="o">,</code> <code class="o">(</code><code class="kt">byte</code><code class="o">)</code><code class="mi">255</code> <code class="o">},</code> <code class="k">new</code> <code class="kt">byte</code><code class="o">[]</code> <code class="o">{</code> <code class="o">(</code><code class="kt">byte</code><code class="o">)</code><code class="mi">0</code><code class="o">,</code> <code class="o">(</code><code class="kt">byte</code><code class="o">)</code><code class="mi">255</code> <code class="o">});</code> <code class="n">image</code> <code class="o">=</code> <code class="k">new</code> <code class="n">BufferedImage</code><code class="o">(</code><code class="n">cm</code><code class="o">,</code> <code class="n">wr</code><code class="o">,</code> <code class="kc">false</code><code class="o">,</code> <code class="kc">null</code><code class="o">);</code> <code class="n">random</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Random</code><code class="o">();</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">run</code><code class="o">()</code> <code class="o">{</code> <code class="k">if</code> <code class="o">(</code> <code class="n">random</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">)</code> <code class="n">initialize</code><code class="o">();</code> <code class="k">while</code> <code class="o">(</code><code class="kc">true</code><code class="o">)</code> <code class="o">{</code> <code class="n">random</code><code class="o">.</code><code class="na">nextBytes</code><code class="o">(</code><code class="n">data</code><code class="o">);</code> <code class="n">repaint</code><code class="o">();</code> <code class="k">try</co