プライマリ・コンテンツに移動
Java Platform, Standard Editionトラブルシューティング・ガイド
リリース10
E94990-01
目次へ移動
目次

前
次

12 Java 2D

Java 2D APIで発生する可能性のある一般的な問題のいくつかをトラブルシューティングするための情報および指針。

この章の構成は、次のとおりです。

Java 2Dプロパティのサマリーについては、「Java 2Dのプロパティ」を参照してください。

一般的なパフォーマンス問題

Java 2Dハードウェア高速化レンダリング・プリミティブに関する一般的なパフォーマンス問題、およびプリミティブ・トレースを検出して非高速化レンダリングを回避する方法。

レンダリング・パフォーマンスの低下には様々な原因が考えられます。次のトピックでは、アプリケーションのレンダリング・パフォーマンスの低下の原因を特定し、ソフトウェアのみのレンダリング・パフォーマンスを改善するためのアプローチを提案します。

このトピックでは、次の項目について説明します。

ハードウェア高速化レンダリング・プリミティブ

パフォーマンス問題の原因についての理解を深めるため、ハードウェア高速化の意味について考えてます。

一般に、ハードウェア高速化レンダリングは2つのカテゴリに分けられます。

  • 「高速化対象」の宛先へのハードウェア高速化レンダリング。ハードウェア高速化が可能なレンダリングの宛先の例として、VolatileImage、画面、およびBufferStrategyがあげられます。宛先が高速化対象である場合、表面に対するレンダリングはビデオ・ハードウェアによって実行される可能性があります。したがって、ユーザーがdrawRect呼出しを発行すると、Java 2Dはその呼出しを、ベースとなるネイティブAPI (GDI、DirectDraw、Direct3D、OpenGL、X11など)にリダイレクトし、そこでハードウェアを使用して操作が実行されます。

  • 高速化メモリー(ビデオ・メモリーまたはピックスマップ)へのイメージのキャッシング。これにより、それらのイメージを別の高速化対象表面に非常に高速でコピーできるようになります。これらのイメージは管理対象イメージと呼ばれます。

理想的には、高速化対象表面で実行される操作はすべて、ハードウェアで高速化されます。この場合、アプリケーションはプラットフォームによって提供されるメリットを最大限に享受できます。

残念ながら多くの場合、デフォルト・パイプラインはレンダリングにハードウェアを使用できません。その原因は、パイプラインの制限やベースとなるネイティブAPIにあります。たとえば、ほとんどのXサーバーは、アンチエイリアス・プリミティブのレンダリングやアルファ合成をサポートしません。

パフォーマンス問題の原因の1つは、実行される操作がハードウェア高速化されない場合にあります。宛先表面が高速化される場合でも、その一部のプリミティブが高速化されない可能性があります。

ハードウェア高速化が使用されていない場合を検出する方法を知ることが重要です。それを知れば、パフォーマンスの改善が容易になる可能性があります。

非高速化レンダリングの検出および回避のためのプリミティブ・トレース

高速化されないレンダリングを検出するには、Java 2Dプリミティブ・トレースを使用できます。

Java 2Dには組込みのプリミティブ・トレースが含まれています。

アプリケーションの実行時に-Dsun.java2d.trace=countを指定します。アプリケーションが終了すると、プリミティブのリストとそれらのカウントがコンソールに出力されます。

MaskBlitプリミティブや任意のGeneral*プリミティブが表示された場合、それは通常、レンダリングの一部がソフトウェア・ループによって処理されていることを意味します。Linux上で、半透明のBufferedImageに対してdrawImageを実行してVolatileImageに書き込む場合の出力を、次に示します。

sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, "Integer BGR Pixmap")sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntBgr)
デフォルト・パイプラインでの一般的な非高速化プリミティブと、トレース出力に示されるそれらの特徴をいくつかあげてみます。

注意:

このトレースのほとんどはLinuxで行われたものです。使用しているプラットフォームや構成によっては多少の違いが生じる可能性があります。
  • 半透明イメージ(ColorModel.getTranslucency()Translucency.TRANSLUCENTが返されるイメージ)、またはAlphaCompositingを使用したイメージ。サンプルのプリミティブ・トレース出力:
    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以外のアルファ値を含むカラー)でのレンダリング、Graphics2D.setComposite()によるデフォルト以外のAlphaCompositingモードの設定、のいずれかによる)。
    sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOver, IntRgb)sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)
    ]
  • 本格的な変換(単なる移動ではない変換)。変換された不透明イメージのVolatileImageへのレンダリング。
    sun.java2d.loops.TransformHelper::TransformHelper(IntBgr, SrcNoEa, IntArgbPre)
    
  • 回転されるラインのレンダリング。
    sun.java2d.loops.DrawPath::DrawPath(AnyColor, SrcNoEa, AnyInt)
    

    アプリケーションをトレース付きで実行し、不要な非高速化プリミティブを使用していないことを確認してください。

低いレンダリング・パフォーマンスの原因

レンダリング・パフォーマンス悪化の原因および可能な代替方法のリスト。

いくつかのレンダリング・パフォーマンスの低下の原因および可能な選択肢を次に示します。

  • 高速化レンダリングと非高速化レンダリングの混在:

    特定のパイプラインでの高速化表面へのレンダリング時に、アプリケーションによってレンダリングされるプリミティブの一部しか高速化できない状況では、スラッシングが発生する可能性があります。パイプラインで常にレンダリング・パフォーマンスの改善に向けた調整が試行されても、成功する可能性がおそらくほとんどないからです。

    レンダリング・プリミティブの大部分が高速化されないことが事前にわかっている場合、BufferedImageにレンダリングしてからバックバッファや画面にコピーするか、あるいは説明したフラグのいずれかを使用して非ハードウェア高速化パイプラインに切り替えたほうが得策です。

    注意:

    このアプローチを採用した場合、Java 2Dでのハードウェア高速化の使用状況が将来改善されても、そのメリットをアプリケーションで享受できなくなる可能性があります。

    たとえば、リモートXサーバーの場合に使用頻度の高いアプリケーション内でアンチエイリアスやアルファ合成などが多用されると、パフォーマンスが極度に低下する可能性があります。これを避けるには、-Dsun.java2d.pmoffscreen=falseプロパティをJavaランタイムに渡すかプログラム内でSystem.setProperty() APIを使用して設定して、ピックスマップの使用を無効にします。

    注意:

    このプロパティは1回のみ読み込まれるので、GUI関連の操作の前に設定する必要があります。
  • 最適でないレンダリング・プリミティブ:

    目的の視覚効果を実現する際に、可能なかぎりもっとも単純なプリミティブを使用することをお薦めします。

    たとえば、Graphics.drawLine()を使用して新しいLine2D().draw()は使用しません。結果は同じようにみえます。ただし、2番目の操作では、通常レンダリング・コストの非常に高い汎用形状としてレンダリングされるため、計算量が格段に多くなります。形状は、プリミティブ・トレース内で、アンチエイリアス設定や特定のパイプラインに応じてさまざまな方法で表示されますが、おそらく多数の*FillSpansまたはDrawPathプリミティブとして表示されます。

    複雑な属性のもう1つの例は、GradientPaintです。これは、デフォルト以外の一部のパイプライン(OpenGLなど)ではハードウェア高速化される可能性がありますが、デフォルト・パイプラインではハードウェア高速化されません。したがって、パフォーマンスの問題が発生する場合にGradientPaintの使用を制限できます。

  • ヒープに基づく出力先のBufferedImage:

    BufferedImageへのレンダリングでは、ほとんど常にソフトウェア・ループが使用されます。

    一部のSPARCシステムでの例外は、特定のイメージング操作を高速化するためにVIS命令セットが使用される場合があることです。VIS命令セットを参照してください。

    レンダリングが高速化される機会を残すには、BufferStrategyまたはVolatileImageオブジェクトをレンダリング先として選択してください。

  • 組込み高速化メカニズムの無効化:

    Java 2Dは特定タイプのイメージを高速化しようとします。VolatileImageなどの高速化宛先へのコピーを高速化するため、イメージの内容がビデオ・メモリーにキャッシュされる可能性があります。これらのメカニズムは、意図せずにアプリケーションで無効化される場合があります。

  • getDataBuffer()によるピクセルへの直接アクセス:

    アプリケーションがgetRaster().getDataBuffer() APIを使用してBufferedImageピクセルにアクセスした場合、Java 2Dは、キャッシュ内のデータが最新であることを保証できないため、このタイプのイメージの高速化の試みをすべて無効にします。

    これを回避するには、getDataBuffer()を呼び出さないでください。代わりに、BufferedImage.getRaster()メソッドで取得可能なWriteableRasterを操作します。

    ピクセルを直接変更する必要がない場合には、イメージをビデオ・メモリー内に手動でキャッシュするためにイメージのキャッシュ・コピーをVolatileImage内に保持し、元のイメージが更新された時点でそのキャッシュ・データを更新します。

  • すべてのコピーの前のスプライト・レンダリング:

    アプリケーションがイメージを高速化表面(VolatileImageBufferStrategy)にコピーする前にイメージにレンダリングする場合、そのイメージは、高速化メモリーにキャッシュされるメリットを享受できません。その理由は、元のイメージが更新されるたびにキャッシュ・コピーを更新する必要があり、そのためにデフォルトのシステムメモリーベースの表面だけが使用されて、高速化されないからです。

  • 高速化メモリー・リソースの枯渇:

    アプリケーションが多数のイメージを使用すると、使用可能な高速化メモリーが使い果たされる可能性があります。これがアプリケーションのパフォーマンス問題の原因となっている場合、リソースを管理しなければいけない可能性があります。

    次のAPIを使えば、使用可能な高速化メモリーの量を要求できます。GraphicsDevice.getAvailableAcceleratedMemory()

    さらに次のAPIを使えば、イメージが高速化されているかどうかを判定できます。Image.getCapabilities()

    アプリケーションがリソースを使い果たしていると判断した場合は、必要のないイメージを保持しないことにより、問題を処理できます。たとえば、ゲームが次のレベルに進んだら、以前のレベルに含まれるイメージをすべて解放します。また、イメージに関連付けられた高速化リソースをImage.flush() APIで解放することもできます。

    高速化優先度API Image.getAccelerationPriority()およびsetAccelerationPriority()を使用してイメージの高速化優先度を指定することもできます。少なくともバック・バッファを高速化することは妥当なので、これを最初に作成し、高速化優先度1 (デフォルト)を指定します。また、必要であれば、高速化優先度を0.0に設定して、特定のイメージが高速化されないようにすることもできます。

ソフトウェアのみのレンダリングのパフォーマンスの改善

ソフトウェアのみのレンダリングのパフォーマンスの改善方法。

アプリケーションが(BufferedImageへのレンダリングのみを行うかデフォルト・パイプラインを非高速化パイプラインに変更したために)ソフトウェア専用レンダリングに依存している場合、あるいはアプリケーションが混在されたレンダリングを行う場合でも、次に示す特定のアプローチによってパフォーマンスが改善します。

  1. 最適化されたサポートとイメージの種類または操作:

    プラットフォーム全体のサイズ制約のために、あるイメージ形式を別の形式に変換するための最適化されたルーチンは、Java 2Dには限られた数しか含まれていません。最適化された直接ループが見つからない状況では、Java 2Dは中間イメージ形式(IntArgb)を介して変換します。この場合、パフォーマンスが低下します。

    Java 2Dプリミティブ・トレースを使用すればそのような状況を検出できます。

    drawImage呼出しに対して2つのプリミティブが存在します。1つ目はイメージをソース形式から中間IntArgb形式に変換し、2つ目は中間IntArgbから変換先の形式に変換します。

    そのような状況を回避する2つの方法を次に示します。

    • 可能であれば別のイメージ形式を使用します。

    • イメージを、よりサポート・レベルの高い形式(INT_RGBINT_ARGBなど)の中間イメージに変換します。そうすれば、カスタム・イメージ形式からの変換が、コピーごとに発生する代わりに一度だけ発生するようになります。

  2. 透明と半透明:

    できれば、完全な半透明(INT_ARGBなど)のイメージではなく、1ビット透明(BITMASK)のイメージをスプライトとして使用することを検討してください。

    完全なアルファを持つイメージの処理では、CPUの負荷がより高くなります。

    1ビット透明イメージを取得するには、GraphicsConfiguration.createCompatibleImage(w,h, Transparency.BITMASK)の呼出しを使用します。

テキスト関連の問題

テキストのレンダリングに関連して発生する可能性がある問題とクラッシュ、およびそのような問題を克服するためのヒントの説明。

この項には次のサブセクションが含まれます:

テキスト・レンダリング中のアプリケーション・クラッシュ

テキスト・レンダリング中にアプリケーションがクラッシュする場合は、まず致命的エラー・ログ・ファイルをチェックしてください。

このエラー・ログ・ファイルの詳細は、「致命的エラー・ログ」を参照してください。fontmanager.dll内でクラッシュが発生した場合やスタック内にfontmanagerが存在している場合は、フォント処理コード内でクラッシュが発生したことになります。次の例は、一般的なネイティブ・スタック・フレーム(完全なログ・ファイルからの抜粋)を示しています。

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

この場合、特定のフォントがおそらく問題です。その場合は、このフォントをシステムから削除すれば、おそらく問題が解決します。

フォント・ファイルを特定するには、アプリケーションの実行時に-Dsun.java2d.debugfonts=trueを指定します。次の例に示すように、通常、最後に示されたフォントが、問題の原因となっているフォントです。

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

注意:

場合によっては、最後に言及されたフォントが問題ではない可能性があります。フォント名が出力されるのは初回使用時であり、その後の使用では表示されません。

この特定のフォントが問題の原因であることを確認するには、そのフォントをシステムから一時的に削除します。この特定のファミリ名に関連付けられたファイル名は、出力から容易にわかります。

もう1つの確認方法は、Font2DTestツール(demo/jfc/Font2DTest)を使用して疑わしいフォントをテストすることです。特定のフォントのサイズ、スタイル、およびラスター化モードを指定できます。Font2DTestで特定のフォントを表示させる途中でJDKがクラッシュした場合、そのフォントが、問題の原因となったフォントである可能性が非常に高くなります。

あるフォントがJDKのクラッシュを引き起こしたことがわかった場合、特定のフォントとオペレーティング・システムを含めて、この問題をバグ・データベースに報告することは非常に重要です。「バグ・レポートの提出」を参照してください。

テキストの外観の違い

Javaは独自のフォント・ラスタライザを備えているため、Javaアプリケーションとネイティブ・アプリケーションとでテキストの外観に何らかの小さな違いがあることが予想できます。

こうした相違点の一般的な原因の1つは、アンチエイリアス設定が違う可能性があることです。特に、Swingアプリケーションは、Linuxデスクトップのフォント・アンチエイリアス設定を無視する場合があります。

この動作には、考えられる理由がいくつかあります。

  • リモートX11経由の場合、パフォーマンス上の理由により、アンチエイリアスがデフォルトでは有効になりません。Java 2D FAQのフォントおよびテストに関する質問に関する項を参照してください。

  • 埋込みビットマップを使用するCJKフォントは、サブピクセル・テキストの代わりにビットマップを使ってレンダリングされる可能性があります。

  • サポートされていないデスクトップの中には、そのフォント・スムージング設定が適切に報告されないものがあります。たとえば、KDEはサポートされていませんが、たいていは動作するはずです。しかし、なんらかの問題によってJDKはその設定を検出できないようです。

構成が予想どおりのものであることを確認する最良の方法は、Font2DTestを実行し、ネイティブ・アプリケーションで使用されているフォントを明示的に選択し、ほかのパラメータを必要に応じて設定することです。図12-1は、Font2DTestツールのサンプル画面です。

図12-1 Font2DTestツールのサンプル画面

図12-1の説明が続きます
「図12-1 Font2DTestツールのサンプル画面」の説明

ヒント:

ユーザー独自の文字列を入力するには、Text to useというラベルの付いたドロップ・ダウン・リストで「User Text」を選択します。

Java言語のフォントのサイズは常に72 dpiで表現されます。ネイティブOSでは別の画面dpiを使用できるため、調整が必要になります。対応するJavaフォント・サイズを計算するには、Toolkit.getScreenResolution()を72で割り、その結果にネイティブ・フォントのサイズを掛けます。

Windowsルック・アンド・フィールや(Oracle SolarisおよびLinuxオペレーティング・システム用の) GTKルック・アンド・フィールなど、すべてのネイティブSwingルック・アンド・フィールで、Swingコンポーネントによってこの調整が自動的に実行されますが、Font2DTestを実行する場合、そのテキスト表示領域では常に72 dpiが使用されます。

Windows以外のオペレーティング・システムでは通常、Type1フォントではなくTrueTypeフォントの使用をお薦めします。フォント・タイプを知るもっとも簡単な方法はファイル拡張子を調べることです。pfaおよびpfbという拡張子はType1フォントを表し、ttf、ttc、およびtteという拡張子はTrueTypeフォントを表します。

メトリック

テキストの境界が予想していたものと異なることに気づいた場合、境界の適切な計算方法を使用していることを確認してください。たとえば、FontMetricsから得られる高さは特定のテキストに固有のものではなく、stringWidthは論理的な有効幅を示し、と同じものではありません。詳細は、Java 2D FAQのフォントおよびテキストに関する質問を参照してください。

Java 2D印刷

Java 2D印刷で発生する可能性がある問題のリスト。

このセクションでは、Java 2D印刷で発生する可能性のあるいくつかの問題を説明し、その原因と解決方法を提示します。

Java 2D FAQの印刷に関する質問も参照してください。

  1. Windowsでの印刷中にJREがクラッシュする

    原因: JREが使用するWindowsプリンタ・ドライバに問題がある可能性があります。

    解決方法: 使用中のプリンタのWindowsプリンタ・ドライバをアップグレードします。

  2. 印刷が成功しているように見えるが、Windowsでジョブが印刷されない。

    原因: 一部のジョブでは、プリンタへのスプールが正しく行われません。

    解決方法: プリンタ・ドライバのプロパティで、「高度な印刷のオプション」を無効にします。

  3. Windowsに印刷ダイアログ・ボックスが表示されるまでに長い時間がかかる。

    原因: アプリケーションが、切断されているプリンタも含め、すべてのプリンタをJREにプローブさせている可能性があります。

    解決方法: 切断されているか到達不可能なネットワーク・プリンタを探し、それらをプリンタのリストから削除します。

  4. PrintJob.printDialog()がOracle SolarisおよびLinuxで「サービスが見つかりません」というエラーになる。

    原因: 原因は次のいずれかです。

    • lpcユーティリティが/usr/sbinディレクトリにありません。

    • lpstatユーティリティが/usr/sbinディレクトリにありません。

    解決方法: lpclpstatを、前述の標準の場所にインストールします。