Java AWTネイティブ・インタフェース(JAWT)は、Java APIウィンドウとサーフェス、およびプラットフォームのネイティブAPIウィンドウとサーフェス間の相互作用のための標準サポートされた方法を提供するネイティブ(例:C言語ベース) APIの小さなセットで構成されています。 非JavaライブラリはJava所有のウィンドウにレンダリングすることができます。
ノート: この文書では、"Java AWT Native Interface"、"AWTネイティブ・インタフェース"、"JAWT"という用語は同じ意味で使用できます。
JAWTを使用しないネイティブ・レンダリングの基本的な障害は、描画コードが描画する場所を特定できないことです。 ネイティブ・コードは、Java描画サーフェス(Canvasの基になるネイティブIDへのハンドルなど)に関する情報にアクセスする必要がありますが、取得できません。
その情報(JAWTなし)がなければ、アプリケーションはJavaで全く共有されていない独自のトップレベル・ウィンドウを作成することによってのみネイティブ・レンダリングを使用できます。 これは、ほとんどの用途では受け入れられません。 JAWT経由の使用を除いて、これはJavaプラットフォーム実装の内部的なものとみなされます: 非公開、サポートされていない、文書化されていない。JAWTは技術的に可能なすべての面倒な実装でサポートされるべきですが、これはJCKによって強制されません。 APIにはプラットフォーム固有の部分とプラットフォームに依存しない部分があり、各プラットフォームの異なるデータ構造と要件を考慮しています。 このドキュメントでは、プラットフォームに依存しない部分について説明し、Oracle JDKでサポートされているデスクトップ操作環境のプラットフォーム依存部分についても説明します。 AWTの場合、プラットフォームという用語は、デスクトップのウィンドウ環境に比べて、基本となるオペレーティング・システムに結びついていません。
AWT Native Interfaceを使用する理由は次のとおりです。
欠点には
AWTネイティブ・インタフェースの使い方の例は、このドキュメントの後半で説明します。
JAWTの使用方法はJNIに依存
Java Standard Editionの定義には、JNI (Java Native Interface)が含まれます。 多くのJava開発者はこれを使用する必要はありませんが、Java言語プログラムがホスト・プロセッサ・アーキテクチャのネイティブ・マシン命令にコンパイルされたアプリケーション・コードと直接対話する唯一の標準サポートされた方法です。 JNIは、これまで混在言語が必要な場所で使用されています。 これらは決してAWTのようなケースに限定されません。 たとえば、JNIを使用して、USBポート経由でシステムに接続されたスキャナなどの周辺機器とやり取りするネイティブ・コードと統合できます。
したがって、JNIは、ほぼあらゆる種類のネイティブ・ライブラリにアクセスするのに十分一般的です。 このドキュメントの残りの部分では、JNIの使い方に精通しています。
JAWTの使い方
このセクションでは、AWT Native Interfaceの最も一般的な使い方について説明 - paintメソッドをオーバーライドして、描画操作をネイティブ・レンダリング・ライブラリに指示します。レンダリング・ライブラリは、Java VMに問合せして、レンダリングに必要な情報を決定します。 ただし、paintメソッド内のコードだけでなくネイティブ・コードが、AWT Native Interfaceを使ってターゲット描画サーフェスに関する情報を学習することもできます。
ネイティブ・レンダリング・ライブラリをJava Canvasに接続するための最初の手順は、Canvasを継承してpaintメソッドをオーバーライドする新しいクラスを新たに定義することです。 Javaシステムは、ほかのすべてのGUIオブジェクトの場合と同様に、Canvasオブジェクトのためのすべての描画オペレーションをpaintメソッドで経路指定します。 キャンバスは、ボタンのようにコンテンツを持たないため、レンダリング・サーフェスに適しています。
新しいpaintメソッドは、ネイティブ・レンダリング・ライブラリ内に実装するために、public native voidとして宣言する必要があります。ネイティブ・ライブラリ自体は、クラスのstaticブロック内でSystem.loadLibrary( "myRenderingLib")呼出しを含めることによって、実行時にロードされます。 myRenderingLib名はネイティブ共有ライブラリに使用されます。LinuxまたはSolarisオペレーティング環境の場合、ディスク上のライブラリ・ファイルの実際の名前はlibmyRenderingLib.soです。
そのようなクラスの簡単な例を示します。
import java.awt.*; import java.awt.event.*; public class MyCanvas extends Canvas { static { System.loadLibrary("myRenderingLib"); } public native void paint(Graphics g); public static void main(String[] args) { Frame f = new Frame(); f.setBounds(0, 0, 500, 110); f.add(new MyCanvas()); f.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent ev) { System.exit(0); } } ); f.show(); } }
このクラスには、このコードをテスト目的のアプリケーションとして実行するために使用できるmainメソッドがあります。
次のステップは、上記のMyCanvasクラス・ファイルでjavahツールを実行して、Javaが使用すると予想されるネイティブpaintメソッドへのインタフェースを記述するC/C++ヘッダー・ファイルを生成することです。javahは、JDKに含まれる標準ツールです。 NB: javac -h outputdirも使用できます。
最後のステップと最も興味深いのは、javahが生成したヘッダー・ファイルに準拠したインタフェースでネイティブ・レンダリング・メソッドを作成し、それをリンクして標準共有ライブラリ(上記の例ではmyRenderingLibと呼ばれています)として、適切なJDK提供の$JDK_HOME/lib/$JAWT_LIBライブラリ・ターゲット・プラットフォーム。 JAWT_LIBはベース名"jawt"を持ち、プラットフォームの共有オブジェクト命名規則に従います。すなわち:
次に、X11ベースの描画環境(LinuxまたはSolaris)およびAWT Native Interfaceが存在するJava VMで使用するために設計されたネイティブpaintメソッドのサンプル・ソース・コードを示します:
#include "MyCanvas.h" #include "jawt_md.h" /* * Class: MyCanvas * Method: paint * Signature: (Ljava/awt/Graphics;)V */ JNIEXPORT void JNICALL Java_MyCanvas_paint (JNIEnv* env, jobject canvas, jobject graphics) { JAWT awt; JAWT_DrawingSurface* ds; JAWT_DrawingSurfaceInfo* dsi; JAWT_X11DrawingSurfaceInfo* dsi_x11; jboolean result; jint lock; GC gc; short i; char *testString = "^^^ rendered from native code ^^^"; /* Get the AWT */ awt.version = JAWT_VERSION_9; if (JAWT_GetAWT(env, &awt) == JNI_FALSE) { printf("AWT Not found "); return; } /* Get the drawing surface */ ds = awt.GetDrawingSurface(env, canvas); if (ds == NULL) { printf("NULL drawing surface "); return; } /* Lock the drawing surface */ lock = ds->Lock(ds); if((lock & JAWT_LOCK_ERROR) != 0) { printf("Error locking surface "); awt.FreeDrawingSurface(ds); return; } /* Get the drawing surface info */ dsi = ds->GetDrawingSurfaceInfo(ds); if (dsi == NULL) { printf("Error getting surface info "); ds->Unlock(ds); awt.FreeDrawingSurface(ds); return; } /* Get the platform-specific drawing info */ dsi_x11 = (JAWT_X11DrawingSurfaceInfo*)dsi->platformInfo; /* Now paint */ gc = XCreateGC(dsi_x11->display, dsi_x11->drawable, 0, 0); XSetBackground(dsi_x11->display, gc, 0); for (i=0; i<36;i++) { XSetForeground(dsi_x11->display, gc, 10*i); XFillRectangle(dsi_x11->display, dsi_x11->drawable, gc, 10*i, 5, 90, 90); } XSetForeground(dsi_x11->display, gc, 155); XDrawImageString(dsi_x11->display, dsi_x11->drawable, gc, 100, 110, testString, strlen(testString)); XFreeGC(dsi_x11->display, gc); /* Free the drawing surface info */ ds->FreeDrawingSurfaceInfo(dsi); /* Unlock the drawing surface */ ds->Unlock(ds); /* Free the drawing surface */ awt.FreeDrawingSurface(ds); }
ここでの重要なデータ構造は、jawt.h (jawt_md.hによって取り込まれる)に定義されている、JAWTです。これは、ジョブを完了するためにネイティブ・コードが必要とするすべての情報へのアクセスを提供します。 ネイティブ・メソッドの最初の部分はボイラープレートです。JAWT構造を取得し、JAWT_DrawingSurface構造を取得し、サーフェスをロック(一度に1つの描画エンジンのみにしてください)してから、必要なプラットフォーム固有描画情報へのポインタ(platformInfoフィールド内)を含むJAWT_DrawingSurfaceInfo構造を取得します。 描画サーフェスの境界矩形および現在のクリッピング領域も含んでいます。
platformInfoによってポイントされている情報の構造は、jawt_md.hと呼ばれるマシン依存ヘッダー・ファイルに定義されています。 X11図面では、MyCanvasに関連付けられたX11ディスプレイおよびX11ドロウアブルに関する情報が含まれています。 描画オペレーションが完了したあとに、JAWT_DrawingSurfaceInfoが解放されて、JAWT_DrawingSurfaceがロック解除および解放されると、ボイラープレート・コードが増えています。
Microsoft Windowsプラットフォーム上のGDI APIの対応するコードも同様に構成されますが、Microsoft Windowsの場合はjawt_md.hのバージョンが含まれ、描画サーフェス情報のplatformInfoフィールドにある構造はJAWT_Win32DrawingSurfaceInfo*としてキャストされます。 また、実際の描画オペレーションはもちろん、Microsoft Windowsプラットフォームに適したものに変更される必要があります。 MacOSでも同じです。
サマリー
ネイティブ・コード・ライブラリからJava Canvasに直接レンダリングできることは、レガシー・ソフトウェア・システム(特に、高性能レンダリング・エンジンを含むもの)をJavaに移行することを計画している開発者にとって非常に役立ちます。 これにより、段階的な移行がかなり容易になります(パフォーマンスが重要なレンダリング・コードはそのままにし、それほど重要でないその他のコード部分がJavaに変換される)。 結果は、最新のJavaセントリック・アプリケーションになる可能性があります(移植性と開発効率性の利点を提供する一方、重要なネイティブ・コードのパフォーマンスへの投資を犠牲にしない)。
参照
Java Native Interfaceに関しては、Sheng Liang著『The Java Native Interface: Programmer's Guide and Specification』を参照してください。 この書籍は、1999年6月にAddison-Wesleyから出版されました (ISBN: 0-201-32577-2)。
付録
jawt.hおよびjawt_md.h用のヘッダー・ファイル
jawt.h
#ifndef _JAVASOFT_JAWT_H_ #define _JAVASOFT_JAWT_H_ #include "jni.h" #ifdef __cplusplus extern "C" { #endif /* * AWT native interface. * * The AWT native interface allows a native C or C++ application a means * by which to access native structures in AWT. This is to facilitate moving * legacy C and C++ applications to Java and to target the needs of the * developers who need to do their own native rendering to canvases * for performance or other reasons. * * Conversely it also provides mechanisms for an application which already * has a native window to provide that to AWT for AWT rendering. * * Since every platform may be different in its native data structures * and APIs for windowing systems the application must necessarily * provided per-platform source and compile and deliver per-platform * native code to use this API. * * These interfaces are not part of the Java SE specification and * a VM is not required to implement this API. However it is strongly * recommended that all implementations which support headful AWT * also support these interfaces. * */ /* * AWT Native Drawing Surface (JAWT_DrawingSurface). * * For each platform, there is a native drawing surface structure. This * platform-specific structure can be found in jawt_md.h. It is recommended * that additional platforms follow the same model. It is also recommended * that VMs on all platforms support the existing structures in jawt_md.h. * ******************* * EXAMPLE OF USAGE: ******************* * * On Microsoft Windows, a programmer wishes to access the HWND of a canvas * to perform native rendering into it. The programmer has declared the * paint() method for their canvas subclass to be native: * * * MyCanvas.java: * * import java.awt.*; * * public class MyCanvas extends Canvas { * * static { * System.loadLibrary("mylib"); * } * * public native void paint(Graphics g); * } * * * myfile.c: * * #include "jawt_md.h" * #include <assert.h> * * JNIEXPORT void JNICALL * Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics) * { * JAWT awt; * JAWT_DrawingSurface* ds; * JAWT_DrawingSurfaceInfo* dsi; * JAWT_Win32DrawingSurfaceInfo* dsi_win; * jboolean result; * jint lock; * * // Get the AWT. Request version 9 to access features in that release. * awt.version = JAWT_VERSION_9; * result = JAWT_GetAWT(env, &awt); * assert(result != JNI_FALSE); * * // Get the drawing surface * ds = awt.GetDrawingSurface(env, canvas); * assert(ds != NULL); * * // Lock the drawing surface * lock = ds->Lock(ds); * assert((lock & JAWT_LOCK_ERROR) == 0); * * // Get the drawing surface info * dsi = ds->GetDrawingSurfaceInfo(ds); * * // Get the platform-specific drawing info * dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo; * * ////////////////////////////// * // !!! DO PAINTING HERE !!! // * ////////////////////////////// * * // Free the drawing surface info * ds->FreeDrawingSurfaceInfo(dsi); * * // Unlock the drawing surface * ds->Unlock(ds); * * // Free the drawing surface * awt.FreeDrawingSurface(ds); * } * */ /* * JAWT_Rectangle * Structure for a native rectangle. */ typedef struct jawt_Rectangle { jint x; jint y; jint width; jint height; } JAWT_Rectangle; struct jawt_DrawingSurface; /* * JAWT_DrawingSurfaceInfo * Structure for containing the underlying drawing information of a component. */ typedef struct jawt_DrawingSurfaceInfo { /* * Pointer to the platform-specific information. This can be safely * cast to a JAWT_Win32DrawingSurfaceInfo on Microsoft Windows or a * JAWT_X11DrawingSurfaceInfo on Linux and Solaris. On MacOS this is a * pointer to a NSObject that conforms to the JAWT_SurfaceLayers protocol. * See jawt_md.h for details. */ void* platformInfo; /* Cached pointer to the underlying drawing surface */ struct jawt_DrawingSurface* ds; /* Bounding rectangle of the drawing surface */ JAWT_Rectangle bounds; /* Number of rectangles in the clip */ jint clipSize; /* Clip rectangle array */ JAWT_Rectangle* clip; } JAWT_DrawingSurfaceInfo; #define JAWT_LOCK_ERROR 0x00000001 #define JAWT_LOCK_CLIP_CHANGED 0x00000002 #define JAWT_LOCK_BOUNDS_CHANGED 0x00000004 #define JAWT_LOCK_SURFACE_CHANGED 0x00000008 /* * JAWT_DrawingSurface * Structure for containing the underlying drawing information of a component. * All operations on a JAWT_DrawingSurface MUST be performed from the same * thread as the call to GetDrawingSurface. */ typedef struct jawt_DrawingSurface { /* Cached reference to the Java environment of the calling thread. * If Lock(), Unlock(), GetDrawingSurfaceInfo() or * FreeDrawingSurfaceInfo() are called from a different thread, * this data member should be set before calling those functions. */ JNIEnv* env; /* Cached reference to the target object */ jobject target; /* * Lock the surface of the target component for native rendering. * When finished drawing, the surface must be unlocked with * Unlock(). This function returns a bitmask with one or more of the * following values: * * JAWT_LOCK_ERROR - When an error has occurred and the surface could not * be locked. * * JAWT_LOCK_CLIP_CHANGED - When the clip region has changed. * * JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed. * * JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed */ jint (JNICALL *Lock) (struct jawt_DrawingSurface* ds); /* * Get the drawing surface info. * The value returned may be cached, but the values may change if * additional calls to Lock() or Unlock() are made. * Lock() must be called before this can return a valid value. * Returns NULL if an error has occurred. * When finished with the returned value, FreeDrawingSurfaceInfo must be * called. */ JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo) (struct jawt_DrawingSurface* ds); /* * Free the drawing surface info. */ void (JNICALL *FreeDrawingSurfaceInfo) (JAWT_DrawingSurfaceInfo* dsi); /* * Unlock the drawing surface of the target component for native rendering. */ void (JNICALL *Unlock) (struct jawt_DrawingSurface* ds); } JAWT_DrawingSurface; /* * JAWT * Structure for containing native AWT functions. */ typedef struct jawt { /* * Version of this structure. This must always be set before * calling JAWT_GetAWT(). It affects the functions returned. * Must be one of the known pre-defined versions. */ jint version; /* * Return a drawing surface from a target jobject. This value * may be cached. * Returns NULL if an error has occurred. * Target must be a java.awt.Component (should be a Canvas * or Window for native rendering). * FreeDrawingSurface() must be called when finished with the * returned JAWT_DrawingSurface. */ JAWT_DrawingSurface* (JNICALL *GetDrawingSurface) (JNIEnv* env, jobject target); /* * Free the drawing surface allocated in GetDrawingSurface. */ void (JNICALL *FreeDrawingSurface) (JAWT_DrawingSurface* ds); /* * Since 1.4 * Locks the entire AWT for synchronization purposes */ void (JNICALL *Lock)(JNIEnv* env); /* * Since 1.4 * Unlocks the entire AWT for synchronization purposes */ void (JNICALL *Unlock)(JNIEnv* env); /* * Since 1.4 * Returns a reference to a java.awt.Component from a native * platform handle. On Windows, this corresponds to an HWND; * on Solaris and Linux, this is a Drawable. For other platforms, * see the appropriate machine-dependent header file for a description. * The reference returned by this function is a local * reference that is only valid in this environment. * This function returns a NULL reference if no component could be * found with matching platform information. */ jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo); /** * Since 9 * Creates a java.awt.Frame placed in a native container. Container is * referenced by the native platform handle. For example on Windows this * corresponds to an HWND. For other platforms, see the appropriate * machine-dependent header file for a description. The reference returned * by this function is a local reference that is only valid in this * environment. This function returns a NULL reference if no frame could be * created with matching platform information. */ jobject (JNICALL *CreateEmbeddedFrame) (JNIEnv *env, void* platformInfo); /** * Since 9 * Moves and resizes the embedded frame. The new location of the top-left * corner is specified by x and y parameters relative to the native parent * component. The new size is specified by width and height. * * The embedded frame should be created by CreateEmbeddedFrame() method, or * this function will not have any effect. * * java.awt.Component.setLocation() and java.awt.Component.setBounds() for * EmbeddedFrame really don't move it within the native parent. These * methods always locate the embedded frame at (0, 0) for backward * compatibility. To allow moving embedded frames this method was * introduced, and it works just the same way as setLocation() and * setBounds() for usual, non-embedded components. * * Using usual get/setLocation() and get/setBounds() together with this new * method is not recommended. */ void (JNICALL *SetBounds) (JNIEnv *env, jobject embeddedFrame, jint x, jint y, jint w, jint h); /** * Since 9 * Synthesize a native message to activate or deactivate an EmbeddedFrame * window depending on the value of parameter doActivate, if "true" * activates the window; otherwise, deactivates the window. * * The embedded frame should be created by CreateEmbeddedFrame() method, or * this function will not have any effect. */ void (JNICALL *SynthesizeWindowActivation) (JNIEnv *env, jobject embeddedFrame, jboolean doActivate); } JAWT; /* * Get the AWT native structure. This function returns JNI_FALSE if * an error occurs. */ _JNI_IMPORT_OR_EXPORT_ jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt); /* * Specify one of these constants as the JAWT.version * Specifying an earlier version will limit the available functions to * those provided in that earlier version of JAWT. * See the "Since" note on each API. Methods with no "Since" * may be presumed to be present in JAWT_VERSION_1_3. */ #define JAWT_VERSION_1_3 0x00010003 #define JAWT_VERSION_1_4 0x00010004 #define JAWT_VERSION_1_7 0x00010007 #define JAWT_VERSION_9 0x00090000 #ifdef __cplusplus } /* extern "C" */ #endif #endif /* !_JAVASOFT_JAWT_H_ */
jawt_md.h (Linux/Solaris/X11オペレーティング環境のバージョン)
#ifndef _JAVASOFT_JAWT_MD_H_ #define _JAVASOFT_JAWT_MD_H_ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Intrinsic.h> #include "jawt.h" #ifdef __cplusplus extern "C" { #endif /* * X11-specific declarations for AWT native interface. * See notes in jawt.h for an example of use. */ typedef struct jawt_X11DrawingSurfaceInfo { Drawable drawable; Display* display; VisualID visualID; Colormap colormapID; int depth; } JAWT_X11DrawingSurfaceInfo; #ifdef __cplusplus } #endif #endif /* !_JAVASOFT_JAWT_MD_H_ */
jawt_md.h (Microsoft Windows版)
#ifndef _JAVASOFT_JAWT_MD_H_ #define _JAVASOFT_JAWT_MD_H_ #include <windows.h> #include "jawt.h" #ifdef __cplusplus extern "C" { #endif /* * Microsoft Windows specific declarations for AWT native interface. * See notes in jawt.h for an example of use. */ typedef struct jawt_Win32DrawingSurfaceInfo { /* Native window, DDB, or DIB handle */ union { HWND hwnd; HBITMAP hbitmap; void* pbits; }; /* * This HDC should always be used instead of the HDC returned from * BeginPaint() or any calls to GetDC(). */ HDC hdc; HPALETTE hpalette; } JAWT_Win32DrawingSurfaceInfo; #ifdef __cplusplus } #endif #endif /* !_JAVASOFT_JAWT_MD_H_ */
jawt_md.h (MacOS版)
#ifndef _JAVASOFT_JAWT_MD_H_ #define _JAVASOFT_JAWT_MD_H_ #include "jawt.h" #ifdef __OBJC__ #import#endif #ifdef __cplusplus extern "C" { #endif /* * MacOS specific declarations for AWT native interface. * See notes in jawt.h for an example of use. */ /* * When calling JAWT_GetAWT with a JAWT version less than 1.7, you must pass this * flag or you will not be able to get a valid drawing surface and JAWT_GetAWT will * return false. This is to maintain compatibility with applications that used the * interface with Java 6 which had multiple rendering models. This flag is not necessary * when JAWT version 1.7 or greater is used as this is the only supported rendering mode. * * Example: * JAWT awt; * awt.version = JAWT_VERSION_1_4 | JAWT_MACOSX_USE_CALAYER; * jboolean success = JAWT_GetAWT(env, &awt); */ #define JAWT_MACOSX_USE_CALAYER 0x80000000 /* * When the native Cocoa toolkit is in use, the pointer stored in * JAWT_DrawingSurfaceInfo->platformInfo points to a NSObject that conforms to the * JAWT_SurfaceLayers protocol. Setting the layer property of this object will cause the * specified layer to be overlaid on the Components rectangle. If the window the * Component belongs to has a CALayer attached to it, this layer will be accessible via * the windowLayer property. */ #ifdef __OBJC__ @protocol JAWT_SurfaceLayers @property (readwrite, retain) CALayer *layer; @property (readonly) CALayer *windowLayer; @end #endif #ifdef __cplusplus } #endif #endif /* !_JAVASOFT_JAWT_MD_H_ */