13 Java 2D
この章では、Java 2D APIで発生する可能性のあるもっとも一般的な問題のいくつかをトラブルシューティングするための情報とガイダンスを提供します。
この章の構成は、次のとおりです。
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で行われたものです。使用しているプラットフォームや構成によっては多少の違いが生じる可能性があります。ソフトウェアのみのレンダリングのパフォーマンスの改善
アプリケーションが(BufferedImage
へのレンダリングのみを行うかデフォルト・パイプラインを非高速化パイプラインに変更したために)ソフトウェア専用レンダリングに依存している場合、あるいはアプリケーションが混在されたレンダリングを行う場合でも、次に示す特定のアプローチによってパフォーマンスが改善します。
-
最適化されたサポートとイメージの種類または操作:
プラットフォーム全体のサイズ制約のために、あるイメージ形式を別の形式に変換するための最適化されたルーチンは、Java 2Dには限られた数しか含まれていません。最適化された直接ループが見つからない状況では、Java 2Dは中間イメージ形式(
IntArgb
)を介して変換します。この場合、パフォーマンスが低下します。Java 2Dプリミティブ・トレースを使用すればそのような状況を検出できます。
各
drawImage
呼出しに対して2つのプリミティブが存在します。1つ目はイメージをソース形式から中間IntArgb
形式に変換し、2つ目は中間IntArgb
から変換先の形式に変換します。そのような状況を回避する2つの方法を次に示します。
-
可能であれば別のイメージ形式を使用します。
-
イメージを、よりサポート・レベルの高い形式(
INT_RGB
やINT_ARGB
など)の中間イメージに変換します。そうすれば、カスタム・イメージ形式からの変換が、コピーごとに発生する代わりに一度だけ発生するようになります。
-
-
透明と半透明:
できれば、完全な半透明(
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
ノート:
場合によっては、最後に言及されたフォントが問題ではない可能性があります。フォント名が出力されるのは初回使用時であり、その後の使用では表示されません。
この特定のフォントが問題の原因であることを確認するには、そのフォントをシステムから一時的に削除します。この特定のファミリ名に関連付けられたファイル名は、出力から容易にわかります。
あるフォントがJDKのクラッシュを引き起こしたことがわかった場合、特定のフォントとオペレーティング・システムを含めて、この問題をバグ・データベースに報告することは非常に重要です。「バグ・レポートの提出」を参照してください。
テキストの外観の違い
Javaは独自のフォント・ラスタライザを備えているため、Javaアプリケーションとネイティブ・アプリケーションとでテキストの外観に何らかの小さな違いがあることが予想できます。
こうした相違点の一般的な原因の1つは、アンチエイリアス設定が違う可能性があることです。特に、Swingアプリケーションは、Linuxデスクトップのフォント・アンチエイリアス設定を無視する場合があります。
この動作には、考えられる理由がいくつかあります。
-
リモートX11経由の場合、パフォーマンス上の理由により、アンチエイリアスがデフォルトでは有効になりません。
-
埋込みビットマップを使用するCJKフォントは、サブピクセル・テキストの代わりにビットマップを使ってレンダリングされる可能性があります。
-
サポートされていないデスクトップの中には、そのフォント・スムージング設定が適切に報告されないものがあります。たとえば、KDEはサポートされていませんが、たいていは動作するはずです。しかし、なんらかの問題によってJDKはその設定を検出できないようです。
Java言語のフォントのサイズは常に72 dpiで表現されます。ネイティブOSでは別の画面dpiを使用できるため、調整が必要になります。対応するJavaフォント・サイズを計算するには、Toolkit.getScreenResolution()
を72で割り、その結果にネイティブ・フォントのサイズを掛けます。
Windowsルック・アンド・フィールやLinuxオペレーティング・システム用のGTKルック・アンド・フィールなど、すべてのネイティブSwingルック・アンド・フィールで、Swingコンポーネントによってこの調整が自動的に実行されます。
Windows以外のオペレーティング・システムでは通常、Type1フォントではなくTrueTypeフォントの使用をお薦めします。フォント・タイプを知るもっとも簡単な方法はファイル拡張子を調べることです。pfaおよびpfbという拡張子はType1フォントを表し、ttf、ttc、およびtteという拡張子はTrueTypeフォントを表します。
フォント・メトリックス
テキストの境界が予想していたものと異なることに気づいた場合、境界の適切な計算方法を使用していることを確認してください。たとえば、FontMetrics
から得られる高さは特定のテキストに固有のものではなく、stringWidth
は論理的な有効幅を示し、幅と同じものではありません。詳細は、Java 2D FAQのフォントおよびテキストに関する質問を参照してください。