Information and guidance for troubleshooting some of the most common issues that might be found in the Java 2D API.
This chapter contains the following sections:
For a summary of Java 2D properties, see Java 2D Properties.
Generic performance issues related to Java 2D hardware-accelerated rendering primitives, and how to detect primitive tracing and avoid non-accelerated rendering.
There could be many causes for poor rendering performance. The following topics identify the cause for your applications poor rendering performance and suggests some approaches to improve performance of software-only rendering.
This topic contains the following subsections:
In order to better understand what could be causing performance problems, take a look at what hardware acceleration means.
In general, hardware-accelerated rendering could be divided into two categories.
Hardware-accelerated rendering to an "accelerated" destination. Examples of rendering destinations that can be hardware-accelerated are
VolatileImage, screen and
BufferStrategy. If a destination is accelerated, then rendering goes to a surface may be performed by video hardware. So, if you issue a
drawRect call, Java 2D redirects this call to the underlying native API (such as GDI, DirectDraw, Direct3D or OpenGL, or X11), which performs the operation using hardware.
Caching images in accelerated memory (video memory or pixmaps) so that they can be copied very fast to another accelerated surface. These images are known as managed images.
Ideally, all operations performed on an accelerated surface are hardware-accelerated. In this case, the application takes full advantage of what is offered by the platform.
Unfortunately in many cases the default pipelines are not able to use the hardware for rendering. This can happen due to the pipeline limitations, or the underlying native API. For example, most X servers do not support rendering antialiased primitives, or alpha compositing.
One cause of performance issues is when operations performed are not hardware-accelerated. Even in cases when a destination surface is accelerated, some primitives may not be.
It is important to know how to detect the cases when hardware acceleration is not being used. Knowing this may help in improving performance.
To detect a non-accelerated rendering, you can use Java 2D primitive tracing.
Java 2D has built-in primitive tracing.
Run your application with
-Dsun.java2d.trace=count. When the application exits, a list of primitives and their counts is printed to the console.
Any time you see a
MaskBlit or any of the
General* primitives, it typically means that some of your rendering is going through software loops. Here is the output from performing
drawImage on a translucent
BufferedImage to a
VolatileImage on Linux:
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)
Note:Most of this tracing was taken on Linux; you may see some differences depending on your platform and configuration.
Translucency.TRANSLUCENT), or images with
AlphaCompositing. Sample primitive tracing output:
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb,SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)
sun.java2d.loops.MaskFill::MaskFill(AnyColor, Src, IntBgr)
sun.java2d.loops.DrawGlyphListAA::DrawGlyphListAA(OpaqueColor, SrcNoEa, AnyInt)
sun.java2d.loops.DrawGlyphListLCD::DrawGlyphListLCD(AnyColor, SrcNoEa, IntBgr)
0xff) or by setting a non-default
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOver, IntRgb)sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb) ]
sun.java2d.loops.TransformHelper::TransformHelper(IntBgr, SrcNoEa, IntArgbPre)
sun.java2d.loops.DrawPath::DrawPath(AnyColor, SrcNoEa, AnyInt)
Run your application with tracing and ensure that you do not use unaccelerated primitives unless they are needed.
List of causes of poor rendering performance and possible alternatives.
Some of the possible causes of poor rendering performance and possible alternatives are described as follows:
A situation when only part of the primitives rendered by an application could be accelerated by the particular pipeline when rendering to an accelerated surface can cause thrashing, because the pipelines will be constantly trying to adjust for better rendering performance but with possibly little success.
If it is known beforehand that most of the rendering primitives will not be accelerated, then it could be better to either render to a
BufferedImage and then copy it to the back buffer or the screen, or switch to a non-hardware accelerated pipeline using one of the flags discussed.
This approach may limit your application's ability to take advantage of future improvements in Java 2D's use of hardware acceleration.
-Dsun.java2d.pmoffscreen=falseproperty either by passing it to the Java runtime, or by setting it programmatically using the System.setProperty() API.
Note:This property must be set before any GUI-related operations because it is read only once.
It is preferable to use the simplest primitive possible to achieve the desired visual effect.
For example, use Graphics.drawLine() instead of new Line2D().draw(). The result looks the same. However, the second operation is much more computationally intensive because it is rendered as a generic shape, which is typically much more expensive to render. Shapes show up in different ways in the primitive tracing, depending on antialiasing settings and the specific pipeline, but most likely they will show up as many
Another example of complicated attributes is
GradientPaint. Although it may be hardware accelerated by some of the non-default pipelines (such as OpenGL), it is not hardware accelerated by the default pipelines. Therefore, you can restrict the use of
GradientPaint if it causes performance problems.
Rendering to a
BufferedImage almost always uses software loops.
An exception on some SPARC systems is that the VIS instruction set can be used for accelerating certain imaging operations. See VIS Instruction Set.
To ensure that the rendering has the opportunity of being hardware accelerated, choose a
BufferStrategy or a
VolatileImage object as the rendering destination.
Java 2D attempts to accelerate certain types of images. The contents of images can be cached in video memory for faster copying to accelerated destinations such as
VolatileImages. These mechanisms can be unknowingly defeated by the application.
If an application gets access to
BufferedImage pixels by using the getRaster().getDataBuffer() API, then Java 2D will not be able to guarantee that the data in the cache is up to date, so it will disable any acceleration attempts of this type of image.
To avoid this, do not call getDataBuffer(). Instead, work with
WriteableRaster, which can be obtained with the BufferedImage.getRaster() method.
If you need to modify the pixels directly, then you can manually cache your image in video memory by maintaining the cached copy of your image in a
VolatileImage, and updating the cached data when the original image is touched.
If an application renders to an image before copying it to an accelerated surface (
BufferStrategy), then the image cannot take advantage of being cached in accelerated memory. This is because the cached copy must be updated every time the original image is updated, and therefore only the default system-memory-based surface is used, and this means no acceleration.
If the application uses many images, then it can exhaust the available accelerated memory. If this is the cause of performance issues for your application, then you might need to handle the resources.
The following API can be used to request the amount of available accelerated memory: GraphicsDevice.getAvailableAcceleratedMemory().
In addition, the following API can be used to determine if your image is being accelerated: Image.getCapabilities().
If you determined that your application is exhausting the resources, you can handle the problem by not holding images you no longer need. For example, if your game advanced to the next level, release all images from the previous levels. You can also release accelerated resources associated with an image by using the Image.flush() API.
You can also use the acceleration priority API Image.getAccelerationPriority() and setAccelerationPriority() to specify the acceleration priority for your images. It is a good idea to make sure that at least your back-buffer is accelerated, so create it first, and with acceleration priority of 1 (default). You can also prohibit certain images from being accelerated if needed by setting the acceleration priority to 0.0.
Methods to improve performance of software-only rendering.
If your application relies on software-only rendering (by only rendering to a
BufferedImage, or changing the default pipeline to an unaccelerated one), or even if it does mixed rendering, then the following are certain approaches to improving performance:
Image types or operations with optimized support:
Due to overall platform size constraints, Java 2D has a limited number of optimized routines for converting from one image format to another. In situations where an optimized direct loop can not be found, Java 2D will do the conversion through an intermediate image format (
IntArgb). This results in performance degradation.
Java 2D primitive tracing can be used for detecting such situations.
drawImage call there will be two primitives: the first one converting the image from the source format to an intermediate
IntArgb format and the second one converting from intermediate
IntArgb to the destination format.
Here are two ways to avoid such situations:
Use a different image format if possible.
Convert your image to an intermediate image of one of the better-supported formats, such as
INT_ARGB. In this way the conversion from the custom image format will happen only once instead of on every copy.
Transparency vs translucency:
Consider using 1-bit transparent (
BITMASK) images for your sprites as opposed to images with full translucency (such as
INT_ARGB) if possible.
Processing images with full alpha is more CPU-intensive.
You can get a 1-bit transparent image using a call to GraphicsConfiguration.createCompatibleImage(w,h, Transparency.BITMASK).
Possible issues and crashes that are related to text rendering and describes tips to overcome such issues.
This section contains the following subsections:
If an application crashes during text rendering, first check the fatal error log file.
See Fatal Error Log for detailed information about this error log file. If the crash occurred in
fontmanager.dll or if
fontmanager is present in the stack, then the crash occurred in the font processing code. The following example shows typical native stack frames (excerpt from the full log file).
Stack: [0x008a0000,0x008f0000), sp=0x008ef52c, free space=317k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C [ntdll.dll+0x1888f] C [ntdll.dll+0x18238] C [ntdll.dll+0x11c76] C [MSVCR71.dll+0x16b3] C [MSVCR71.dll+0x16db] C [fontmanager.dll+0x21f9a] C [fontmanager.dll+0x22876] C [fontmanager.dll+0x1de40] C [fontmanager.dll+0x1da94] C [fontmanager.dll+0x48abb] j sun.font.FileFont.getGlyphImage(JI)J+0 j sun.font.FileFontStrike.getGlyphImagePtrs([I[JI)V+92 j sun.font.GlyphList.mapChars(Lsun/java2d/loops/FontInfo;I)Z+37 j sun.font.GlyphList.setFromString(Lsun/java2d/loops/FontInfo;Ljava/lang/String;FF)Z+71 j sun.java2d.pipe.GlyphListPipe.drawString(Lsun/java2d/SunGraphics2D;Ljava/lang/String;DD)V+148 j sun.java2d.SunGraphics2D.drawString(Ljava/lang/String;II)V+60 j FontCrasher.tryFont(Ljava/lang/String;)V+138 j FontCrasher.main([Ljava/lang/String;)V+20 v ~StubRoutines::call_stub
In this case, a particular font is probably the problem. If so, then removing this font from the system will likely resolve the problem.
To identify the font file, execute the application with
-Dsun.java2d.debugfonts=true. The font that is mentioned last is usually the one that is causing problems, as shown in the following example.
INFO: Registered file C:\WINDOWS\Fonts\WINGDING.TTF as font ** TrueType Font: Family=Wingdings Name=Wingdings style=0 fileName=C:\WINDOWS\Fonts\WINGDING.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file SYMBOL.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList INFO: Add to Family Symbol, Font Symbol rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile INFO: Registered file C:\WINDOWS\Fonts\SYMBOL.TTF as font ** TrueType Font: Family=Symbol Name=Symbol style=0 fileName=C:\WINDOWS\Fonts\SYMBOL.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager findFont2D INFO: Search for font: Dialog Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file ARIALBD.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList INFO: Add to Family Arial, Font Arial Bold rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile INFO: Registered file C:\WINDOWS\Fonts\ARIALBD.TTF as font ** TrueType Font: Family=Arial Name=Arial Bold style=1 fileName=C:\WINDOWS\Fonts\ARIALBD.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file WINGDING.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file SYMBOL.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager findFont2D INFO: Search for font: Dialog Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file ARIAL.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager addToFontList INFO: Add to Family Arial, Font Arial rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager registerFontFile INFO: Registered file C:\WINDOWS\Fonts\ARIAL.TTF as font ** TrueType Font: Family=Arial Name=Arial style=0 fileName=C:\WINDOWS\Fonts\ARIAL.TTF rank=2 Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file WINGDING.TTF Aug 16, 2006 10:59:06 PM sun.font.FontManager initialiseDeferredFont INFO: Opening deferred font file SYMBOL.TTF
In some cases, the font that is last mentioned might not be the problem. Font names are printed when they are first used and subsequent uses are not shown.
To verify that this particular font is causing the problem, you can temporarily remove it from your system. You can easily find the file name associated with this particular family name from the output.
Another verification approach is to use the Font2DTest tool (
demo/jfc/Font2DTest) to test fonts that you suspect. You can specify a particular font size, style, and rasterization mode. If the process of viewing a particular font with Font2DTest causes the JDK to crash, then it is very likely that it is the font that is causing the problems.
Java has its own font rasterizer, and you can expect some small differences between the appearance of text in a Java application and in a native application.
One of the typical sources of these differences is that the antialiasing settings can be different. In particular, a Swing application sometimes ignores the Linux desktop font antialiasing settings.
There are several likely reasons for this behavior:
Over the remote X11 antialiasing is not enabled by default for performance reasons. See Font and Test questions in the Java 2D FAQ.
CJK fonts that use embedded bitmaps may render using the bitmaps instead of subpixel text.
Some variants of unsupported desktops do not report their font smoothing settings properly. For example, KDE is unsupported but should generally work; however, some problem seems to prevent JDK from picking up the setting.
The best way to ensure that the configuration is what you expect is to run Font2DTest, explicitly select the font used by the native application, and set other parameters as appropriate. Figure 12-1 is a sample screen from the Font2DTest tool.
Figure 12-1 Sample Screen from Font2DTest Tool
You can input your own string by choosing User Text in the drop-down list labeled Text to use.
The size of the font in the Java language is always expressed with 72 dpi. A native OS can use a different screen dpi, and therefore an adjustment must be made. Matching Java font size can be calculated as
Toolkit.getScreenResolution() divided by 72 multiplied by the size of the native font.
In all native Swing look and feel, such as the Windows look and feel or the GTK look and feel (for Oracle Solaris and Linux operating systems), Swing components perform this adjustment automatically, but if you are running Font2DTest, the text display area will always use 72 dpi.
On operating systems other than Windows, the general recommendation is to use TrueType fonts instead of Type1 fonts. The easiest way to figure out the type of font is to look at the file extension: extensions pfa and pfb indicate Type1 fonts, and ttf, ttc, and tte represent TrueType fonts.
If you find that text bounds are different from what you expect, then ensure that you are using the appropriate way to calculate them. For example, the height obtained from a
FontMetrics is not specific to a particular piece of text, and the
stringWidth indicates logical advance, which is not the same thing as wide. For more details, see the Font and Text questions in the Java 2D FAQ.
List of issues that can happen with Java 2D printing.
This section describes some issues that can happen with Java 2D printing and suggests causes and solutions.
Also, see the Printing questions in the Java 2D FAQ.
Cause: The JRE uses Windows printer drivers, and they might have problems.
Solution: Upgrade the Windows printer driver for the printer that is being used.
Cause: Some jobs fail to properly spool to the printer.
Solution: In the printer driver properties, disable Advanced Printing Options.
Cause: Applications might cause the JRE to probe all printers, including those that are disconnected.
Solution: Look for disconnected or unreachable network printers and remove them from the list of printers.
Cause: The cause is one of the following:
lpc utility is not in the
lpstat utility is not in the
lpstat in the standard location, as previously mentioned.