Java 2D Graphics If you’d rather not use this feature, you can disable it with a call to the following method in the JComponent class: public void setDoubleBuffered(boolean aFlag) This method determines whether double buffering will be used for this component or not. If aFlag is true, then drawing will be performed in an offscreen buffer belonging to either this component or one of its visual parents. If aFlag is false, this component’s drawing will be performed directly on the screen. 14.4 Optimizations Performance tuning is a complex subject I’ve really only scratched the surface in this chapter. The example programs show the relative “cost” of different kinds of operations, but there are several techniques you can use to optimize your applications. Two of these techniques are listed below: prerendering For some applications it makes sense to render transformed shapes, images, or text into offscreen buffers. These can be selectively transferred to the screen during an animation. In many cases, this technique is significantly faster than doing the rendering in real time. It’s a trade-off you buy speed by using more memory. smart update The examples in this chapter erase the offscreen buffer and render the whole thing at every time step. In some applications, it’s possible to redraw just a portion of the entire onscreen component. For example, this technique would be helpful in animating a small character over a large background. Other optimization techniques exist, but they are out of the scope of this book. This chapter gives you the tools you need to examine the cost of different operations in the 2D API.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Tomcat Web Hosting services
Java 2D Graphics image would be to put the four bits of each pixel into an 8-bit byte. In this case, the image data would actually occupy twice as much memory as given in the formula above. The following method calculates the memory usage for a BufferedImage. It retrieves the actual DataBuffer for the image and calculates the size accordingly. Note that this calculates the full size of the image’s data buffers, which is a little more realistic measure of memory usage than the minimum size given by the formula above: public static long getImageSize(BufferedImage image) { DataBuffer db = image.getRaster().getDataBuffer(); int dataType = db.getDataType(); int elementSizeInBits = DataBuffer.getDataTypeSize(dataType); return db.getNumBanks() * db.getSize() * elementSizeInBits / 8; } Managing image memory may be as simple as throwing away images that you don’t need any more.[3] Furthermore, if you have several images that represent different parts of the same data set, you should share a single DataBuffer among the images. DataBuffers really take up space, not Images. [3] Of course, you can’t actually free up the memory yourself. You have to null out references; it’s up to the garbage collector to free up the memory. 14.3.2 Double Buffering Double buffering is a technique that is described fully in Chapter 9. It’s a technique for avoiding image flickering. Basically, your application draws into an offscreen image instead of drawing directly on the screen. The image is then drawn onscreen. Double buffering doubles an application’s memory consumption. Think of it this way: instead of just needing some display memory for a window or component, your application now needs memory for a corresponding offscreen image. You should be especially worried about this if you perform double buffering for a resizeable window. Suppose, for example, that your application has a frame window with a default size of 300 by 200 pixels. An offscreen image for double buffering will be 240K, assuming you use 32 bits per pixel. Suppose the user maximizes the window, to 800 by 600 pixels. The size of the offscreen image jumps to 1.92M. But that’s not the worst of it. Many users have systems that allow for much higher screen resolutions. It’s very possible that a user could maximize your application to his or her 1600 by 1200 desktop. This makes the size of the offscreen image a whopping 7.68M! The moral of the story is to be careful about double buffering. Try to double buffer only small, size- controlled parts of your application. If you have to use double buffering on an entire window, consider making the window a fixed size, or add code to enforce a maximum size for the window. 14.3.3 Swing Components Swing components perform double buffering by default. If you use Swing components, you are already paying the memory penalty that double buffering imposes, and you should be careful about the size of these components, as discussed previously. The good news is that Swing is intelligent about its double buffering. If a component is part of a container that already has an offscreen image, the child component will paint itself into its parent’s offscreen image without creating its own offscreen image. page 271
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics Bilinear Transform Image type Frame Rate (fps) TYPE_INT_RGB 80 TYPE_INT_ARGB 72 TYPE_INT_ARGB_PRE 72 TYPE_3BYTE_BGR 82 TYPE_BYTE_GRAY 80 TYPE_USHORT_GRAY 80 TYPE_USHORT_555_RGB 79 TYPE_USHORT_565_RGB 79 x x TYPE_INT_RGB 37 x x TYPE_INT_ARGB 38 x x TYPE_INT_ARGB_PRE 26 x x TYPE_3BYTE_BGR 24 x x TYPE_BYTE_GRAY 10 x x TYPE_USHORT_GRAY 10 x x TYPE_USHORT_555_RGB 36 x x TYPE_USHORT_565_RGB 36 When you run ImageBouncer, you’ll probably notice the following: It’s expensive to transform the image. Animating the transformed image using bilinear interpolation runs at about 50% of the untransformed image frame rate. Bilinear interpolation yields nicer-looking transformed images, but it takes longer than the “nearest neighbor” algorithm. In this respect, it’s a lot like antialiasing for shapes and text: a trade-off of quality for speed. The rendering engine is optimized for some operations and not for others. For example, the TYPE_3BYTE_BGR image type performs best when the image is not transformed. But when the image is transformed, it’s a terrible performer. Subsequent releases of Java 2 should contain more optimizations. 14.3 Memory One aspect of performance tuning is making your application handle memory nicely. It’s very awkward for your support staff if the only answer they have for some problems is “buy more memory.” An ideal application doesn’t use much memory and doesn’t crash if it can’t get enough memory. In 2D applications, images are the biggest area of concern for memory usage. If your application does any image handling, including double-buffered drawing, you should probably be thinking about memory. 14.3.1 Images Any image in your application uses some memory. The formula is pretty simple: memoryUsed = width height bytesPerPixel This formula gives you a minimum size for the image. Of course, there’s some additional memory for the Image or BufferedImage object itself, but this is small compared to the raw image data. The actual memory used also depends on how the image data is stored. Suppose you had an image with a 16-color palette. Each pixel can be represented with four bits. An inefficient way to store this page 270
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics type = BufferedImage.TYPE_BYTE_GRAY; else if (s.equals(”TYPE_USHORT_GRAY”)) type = BufferedImage.TYPE_USHORT_GRAY; else if (s.equals(”TYPE_USHORT_555_RGB”)) type = BufferedImage.TYPE_USHORT_565_RGB; else if (s.equals(”TYPE_USHORT_565_RGB”)) type = BufferedImage.TYPE_USHORT_565_RGB; else { System.out.println(”Unrecognized type.”); return; } mImage = Utilities.makeBufferedImage(mOriginalImage, type); } protected Checkbox createCheckbox(String label, final int item) { Checkbox check = new Checkbox(label); check.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ie) { setSwitch(item, (ie.getStateChange() == ie.SELECTED)); } }); return check; } public void timeStep() { Dimension d = getSize(); if (mX + mDeltaX < 0) mDeltaX = -mDeltaX; else if (mX + mWidth + mDeltaX >= d.width) mDeltaX = -mDeltaX; if (mY + mDeltaY < 0) mDeltaY = -mDeltaY; else if (mY + mHeight + mDeltaY >= d.height) mDeltaY = -mDeltaY; mX += mDeltaX; mY += mDeltaY; mTheta += Math.PI / 192; if (mTheta > (2 * Math.PI)) mTheta -= (2 * Math.PI); } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D)g; setTransform(g2); setBilinear(g2); // Draw the image. g2.drawImage(mImage, AffineTransform.getTranslateInstance(mX, mY), null); } protected void setTransform(Graphics2D g2) { if (mTransform == false) return; float cx = mX + mWidth / 2; float cy = mY + mHeight / 2; g2.rotate(mTheta, cx, cy); } protected void setBilinear(Graphics2D g2) { if (mBilinear == false) return; g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); } } Table 14-3 lists some frame rates for the ImageBouncer example. Table 14-3, Frame Rates for ImageBouncer page 269
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics typeChoice.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent ie) { String type = typeChoice.getSelectedItem(); bouncer.setImageType(type); } }); f.setVisible(true); } private boolean mBilinear = false; private boolean mTransform = false; public static final int BILINEAR = 1; public static final int TRANSFORM = 3; private float mDeltaX, mDeltaY; private float mX, mY, mWidth, mHeight; private float mTheta; private Image mOriginalImage; private BufferedImage mImage; public ImageBouncer(Image image) { mOriginalImage = image; setImageType(”TYPE_INT_RGB”); Random random = new Random(); mX = random.nextFloat() * 500; mY = random.nextFloat() * 500; mWidth = mImage.getWidth(); mHeight = mImage.getHeight(); mDeltaX = random.nextFloat() * 3; mDeltaY = random.nextFloat() * 3; // Make sure points are within range. addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent ce) { Dimension d = getSize(); if (mX < 0) mX = 0; else if (mX + mWidth >= d.width) mX = d.width - mWidth - 1; if (mY < 0) mY = 0; else if (mY + mHeight >= d.height) mY = d.height - mHeight - 1; } }); } public void setSwitch(int item, boolean value) { switch(item) { case BILINEAR: mBilinear = value; break; case TRANSFORM: mTransform = value; break; default: break; } } public void setImageType(String s) { int type = BufferedImage.TYPE_CUSTOM; if (s.equals(”TYPE_INT_RGB”)) type = BufferedImage.TYPE_INT_RGB; else if (s.equals(”TYPE_INT_ARGB”)) type = BufferedImage.TYPE_INT_ARGB; else if (s.equals(”TYPE_INT_ARGB_PRE”)) type = BufferedImage.TYPE_INT_ARGB_PRE; else if (s.equals(”TYPE_3BYTE_BGR”)) type = BufferedImage.TYPE_3BYTE_BGR; else if (s.equals(”TYPE_BYTE_GRAY”)) page 268
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics 14.2.5 Animated Images I’ve discussed how shapes and text perform under different circumstances. Now let’s take a look at the third graphic type images. There aren’t quite as many variables to fiddle with, mostly because the image contains its own color data. The current Paint of the Graphics2D doesn’t affect how images are drawn. Even antialiasing isn’t a factor. Several aspects of images are important for performance. The following example animates an image, in the same spirit as Bouncer and TextBouncer. The running application is shown in Figure 15.32. A small image bounces around the window. You can choose to transform the image as it is rendered, and you can choose from a short list of image storage types. The transformation is a simple rotation, performed at the center of the image. You can, however, choose the interpolation algorithm that is used when the image is rotated. This algorithm specifies how the colors of the rotated image are determined. The default is the “nearest neighbor” algorithm, which is fast but sloppy. As the example runs, you can see the “jaggies” that result from this algorithm in the borders between the hair on my head and the background of the image. If you instead use “bilinear interpolation” (by checking the Bilinear checkbox), the rotated picture will be higher quality, but the animation will be slower. The combo box lets you choose what basic image type will be rendered. Some image types require color conversions before the image can be rendered on your screen. For example, if the base type is TYPE_BYTE_GRAY and you have a color display, the grayscale pixels of the image will have to be converted to red, green, and blue values before the image can be displayed on the screen. Here’s the code for the ImageBouncer example: import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.*; import java.util.Random; public class ImageBouncer extends AnimationComponent { public static void main(String[] args) { String filename = “knudsen.gif”; if (args.length > 0) filename = args[0]; Image image = Utilities.blockingLoad(filename); final ImageBouncer bouncer = new ImageBouncer(image); Frame f = new AnimationFrame(bouncer); f.setFont(new Font(”Serif”, Font.PLAIN, 12)); Panel controls = new Panel(); controls.add(bouncer.createCheckbox(”Bilinear”, ImageBouncer.BILINEAR)); controls.add(bouncer.createCheckbox(”Transform”,ImageBouncer.TRANSFORM)); final Choice typeChoice = new Choice(); typeChoice.add(”TYPE_INT_RGB”); typeChoice.add(”TYPE_INT_ARGB”); typeChoice.add(”TYPE_INT_ARGB_PRE”); typeChoice.add(”TYPE_3BYTE_BGR”); typeChoice.add(”TYPE_BYTE_GRAY”); typeChoice.add(”TYPE_USHORT_GRAY”); typeChoice.add(”TYPE_USHORT_555_RGB”); typeChoice.add(”TYPE_USHORT_565_RGB”); controls.add(typeChoice); f.add(controls, BorderLayout.NORTH); page 267
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics Dimension d = getSize(); int cx = d.width / 2; int cy = d.height / 2; g2.translate(cx, cy); if (mShear) g2.shear(mShearX, mShearY); if (mRotate) g2.rotate(mTheta); g2.translate(-cx, -cy); } protected void setPaint(Graphics2D g2) { if (mGradient) { GradientPaint gp = new GradientPaint(0, 0, Color.blue, 50, 25, Color.green, true); g2.setPaint(gp); } else g2.setPaint(Color.orange); } protected void drawAxes(Graphics2D g2) { if (mAxes == false) return; g2.setPaint(getForeground()); g2.setStroke(new BasicStroke()); Dimension d = getSize(); int side = 20; int arrow = 4; int w = d.width / 2, h = d.height / 2; g2.drawLine(w - side, h, w + side, h); g2.drawLine(w + side - arrow, h - arrow, w + side, h); g2.drawLine(w, h - side, w, h + side); g2.drawLine(w + arrow, h + side - arrow, w, h + side); } } Table 14-2 summarizes some relevant frame rates for TextBouncer, as measured on my computer. Table 14-2, Frame Rules for TextBouncer Antialiasing Gradient Shear Rotate Axes Font Frame Rate (fps) Arial 94 x Arial 82 x x Arial 73 x x x Arial 29 x x x Arial 66 x x x x Arial 28 x x x x x Arial 32 x x x x x Times New Roman 36 x x x x x Lucida Bright Regular 32 What can you learn from this application? The results of this application are counter-intuitive: Using the gradient paint actually speeds things up when antialiasing is being used. The font used is significant, especially with complex rendering. Although you might expect serif fonts like Times New Roman to be slower than sans serif fonts, this is not the case. Although the shearing transformation costs very little, the rotation is expensive. Remember, performance is likely to vary from one system to another and from one release to another. I suggest you try these applications out for yourself. page 266
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics If you’d rather not use this feature, you can disable it with a call to the following method in the JComponent class: public void setDoubleBuffered(boolean aFlag) This method determines whether double buffering will be used for this component or not. If aFlag is true, then drawing will be performed in an offscreen buffer belonging to either this component or one of its visual parents. If aFlag is false, this component’s drawing will be performed directly on the screen. 14.4 Optimizations Performance tuning is a complex subject I’ve really only scratched the surface in this chapter. The example programs show the relative “cost” of different kinds of operations, but there are several techniques you can use to optimize your applications. Two of these techniques are listed below: prerendering For some applications it makes sense to render transformed shapes, images, or text into offscreen buffers. These can be selectively transferred to the screen during an animation. In many cases, this technique is significantly faster than doing the rendering in real time. It’s a trade-off you buy speed by using more memory. smart update The examples in this chapter erase the offscreen buffer and render the whole thing at every time step. In some applications, it’s possible to redraw just a portion of the entire onscreen component. For example, this technique would be helpful in animating a small character over a large background. Other optimization techniques exist, but they are out of the scope of this book. This chapter gives you the tools you need to examine the cost of different operations in the 2D API.
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Tomcat Web Hosting services
Java 2D Graphics image would be to put the four bits of each pixel into an 8-bit byte. In this case, the image data would actually occupy twice as much memory as given in the formula above. The following method calculates the memory usage for a BufferedImage. It retrieves the actual DataBuffer for the image and calculates the size accordingly. Note that this calculates the full size of the image’s data buffers, which is a little more realistic measure of memory usage than the minimum size given by the formula above: public static long getImageSize(BufferedImage image) { DataBuffer db = image.getRaster().getDataBuffer(); int dataType = db.getDataType(); int elementSizeInBits = DataBuffer.getDataTypeSize(dataType); return db.getNumBanks() * db.getSize() * elementSizeInBits / 8; } Managing image memory may be as simple as throwing away images that you don’t need any more.[3] Furthermore, if you have several images that represent different parts of the same data set, you should share a single DataBuffer among the images. DataBuffers really take up space, not Images. [3] Of course, you can’t actually free up the memory yourself. You have to null out references; it’s up to the garbage collector to free up the memory. 14.3.2 Double Buffering Double buffering is a technique that is described fully in Chapter 9. It’s a technique for avoiding image flickering. Basically, your application draws into an offscreen image instead of drawing directly on the screen. The image is then drawn onscreen. Double buffering doubles an application’s memory consumption. Think of it this way: instead of just needing some display memory for a window or component, your application now needs memory for a corresponding offscreen image. You should be especially worried about this if you perform double buffering for a resizeable window. Suppose, for example, that your application has a frame window with a default size of 300 by 200 pixels. An offscreen image for double buffering will be 240K, assuming you use 32 bits per pixel. Suppose the user maximizes the window, to 800 by 600 pixels. The size of the offscreen image jumps to 1.92M. But that’s not the worst of it. Many users have systems that allow for much higher screen resolutions. It’s very possible that a user could maximize your application to his or her 1600 by 1200 desktop. This makes the size of the offscreen image a whopping 7.68M! The moral of the story is to be careful about double buffering. Try to double buffer only small, size- controlled parts of your application. If you have to use double buffering on an entire window, consider making the window a fixed size, or add code to enforce a maximum size for the window. 14.3.3 Swing Components Swing components perform double buffering by default. If you use Swing components, you are already paying the memory penalty that double buffering imposes, and you should be careful about the size of these components, as discussed previously. The good news is that Swing is intelligent about its double buffering. If a component is part of a container that already has an offscreen image, the child component will paint itself into its parent’s offscreen image without creating its own offscreen image. page 271
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics Bilinear Transform Image type Frame Rate (fps) TYPE_INT_RGB 80 TYPE_INT_ARGB 72 TYPE_INT_ARGB_PRE 72 TYPE_3BYTE_BGR 82 TYPE_BYTE_GRAY 80 TYPE_USHORT_GRAY 80 TYPE_USHORT_555_RGB 79 TYPE_USHORT_565_RGB 79 x x TYPE_INT_RGB 37 x x TYPE_INT_ARGB 38 x x TYPE_INT_ARGB_PRE 26 x x TYPE_3BYTE_BGR 24 x x TYPE_BYTE_GRAY 10 x x TYPE_USHORT_GRAY 10 x x TYPE_USHORT_555_RGB 36 x x TYPE_USHORT_565_RGB 36 When you run ImageBouncer, you’ll probably notice the following: It’s expensive to transform the image. Animating the transformed image using bilinear interpolation runs at about 50% of the untransformed image frame rate. Bilinear interpolation yields nicer-looking transformed images, but it takes longer than the “nearest neighbor” algorithm. In this respect, it’s a lot like antialiasing for shapes and text: a trade-off of quality for speed. The rendering engine is optimized for some operations and not for others. For example, the TYPE_3BYTE_BGR image type performs best when the image is not transformed. But when the image is transformed, it’s a terrible performer. Subsequent releases of Java 2 should contain more optimizations. 14.3 Memory One aspect of performance tuning is making your application handle memory nicely. It’s very awkward for your support staff if the only answer they have for some problems is “buy more memory.” An ideal application doesn’t use much memory and doesn’t crash if it can’t get enough memory. In 2D applications, images are the biggest area of concern for memory usage. If your application does any image handling, including double-buffered drawing, you should probably be thinking about memory. 14.3.1 Images Any image in your application uses some memory. The formula is pretty simple: memoryUsed = width height bytesPerPixel This formula gives you a minimum size for the image. Of course, there’s some additional memory for the Image or BufferedImage object itself, but this is small compared to the raw image data. The actual memory used also depends on how the image data is stored. Suppose you had an image with a 16-color palette. Each pixel can be represented with four bits. An inefficient way to store this page 270
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services