AWT Native Interface (original) (raw)

Home Page

The Key to Rendering to an AWT Canvas from Native Code

The new Java SE AWT Native Interface enables rendering libraries compiled to native code to draw directly to a JavaCanvas drawing surface. This means that such libraries can be used without being converted to Java first and without significant impact on performance. An example illustrating how easy it is to use the AWT Native Interface is presented and discussed in this technical note.

Introduction

The definition of Java Standard Edition includes JNI, the Java Native Interface. Most Java developers will never need to use it, but the interface is invaluable in certain situations because it provides the only way for Java byte code to interact directly with application code that has been compiled to the native machine instructions for the host microprocessor. JNI is most often used as an ``escape valve'' to enable access to platform functionality not yet supported by the Java programming language. For example, you could use JNI to integrate with native code that communicates with a peripheral device, such as a scanner, connected to a system via a USB port.

Of course, JNI is general enough to be used to access almost any sort of native library, regardless of whether the task to be accomplished could also be done using pure Java. The major penalty for using it is that code portability suffers, but this may be acceptable or even necessary for business or technical reasons.

Business reasons? Consider the situation where the legacy software you are trying to port to Java uses a third-party library for a critical set of operations. If you do not have source rights to this library and you cannot convince the owner to provide a Java version, you may not be able to use it. Even if you do have the source, the effort needed to port a standard library to Java and test it may be too expensive to consider.

Another important reason for leaving the native code alone is related to performance. If you are dealing with a finely crafted piece of code, carefully tuned for performance over the course of years, you probably do not want to convert it to Java and risk a performance penalty. It is usually best to keep it intact until you are satisfied that the benefits of Java portability and code maintainability outweigh the expected performance difference.

A rendering library is a good example of a piece of native code that most developers would just as soon leave alone for performance reasons. Unfortunately, this is the very type of library that has been most difficult to integrate with Java code through JNI. The fundamental problem has been that the rendering code cannot identify where to draw. It needs access to information about a Java drawing surface (such as a handle to the underlying peer of aCanvas), but cannot get it.

Until now, the Java platform has kept access to this information private — "private" in the sense of undocumented, unsupported, and deprecated. The good news is that this situation will be remedied with the introduction of the "AWT Native Interface" in the Java upgrade release ("Kestrel"). For the first time there will be an official way to obtain all the information you need to know about the peer of a Java Canvas so that you can draw directly to the Canvas from a native code library using the drawing routines provided by the operating system.

How It Works

In this section we'll describe the most common usage of the AWT Native Interface — overriding the paint method to direct drawing operations to a native rendering library which then queries the Java VM to determine the information it needs in order to render. Note, however, that any native code may use the AWT Native Interface to learn about a target drawing surface, not just code in a paint method.

The first step in hooking up a native rendering library to a Java Canvas is to define a new class that extendsCanvas and overrides the paint method. The Java system routes all drawing operations for a Canvas object through the paint method, as it does for all other GUI objects.

The new paint method, to be implemented in the native rendering library, must be declared as public native void, and the native library itself is loaded at runtime by including a call to System.loadLibrary( "myRenderingLib")in the static block of the class. The myRenderingLibname is used for the native shared library; for the Solaris operating environment, the actual name for the library file on disk is libmyRenderingLib.so .

Here is a simple example of such a class:

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();
}

}

Note that this class has a main method that can be used torun this code as an application for testing purposes.

The next step is to run the javah tool on theMyCanvas class file above to generate a C/C++ header file that describes the interface to the native paint method that Java expects to be used. javah is a standard tool included with the Java SDK.

The final step ­ the most interesting one ­ is to write the native rendering method, with an interface that conforms to the header file that javah generated, and build it as a standard shared library (called myRenderingLib in the above example) by linking it, for the Solaris operating environment, against the jre/lib/sparc/libjawt.so library. (For Microsoft Windows, link against the jre/bin/jawt.dlllibrary.) This code will call back to the Java virtual machine to get the drawing surface information it needs to access theMyCanvas peer. Once this information is available, the code can draw directly to MyCanvas using standard drawing routines supplied by the underlying operating system.

Here is sample source code for a native paint method designed for use in a Solaris X11-based drawing environment and a Java VM where the AWT Native Interface is present:

#include "MyCanvas.h" #include "jawt_md.h"

/*

}

The key data structure here is JAWT , which is defined in jawt.h (included by jawt_md.h) ; it provides access to all the information the native code needs to get the job done. The first part of the native method is boilerplate: it populates the JAWT structure, gets aJAWT_DrawingSurface structure, locks the surface (only one drawing engine at a time, please!), then gets aJAWT_DrawingSurfaceInfo structure that contains a pointer (in the platformInfo field) to the necessary platform-specific drawing information. It also includes the bounding rectangle of the drawing surface and the current clipping region.

The structure of the information pointed to byplatformInfo is defined in a machine-dependent header file called jawt_md.h. For Solaris/X11 drawing, it includes information about the X11 display and X11 drawable associated withMyCanvas. After the drawing operations are completed, there is more boilerplate code as JAWT_DrawingSurfaceInfois freed and JAWT_DrawingSurface is unlocked and freed.

The corresponding code for the Microsoft Windows platform would be structured similarly, but would include the version ofjawt_md.h for Microsoft Windows and the structure located in the platformInfo field of drawing surface info would be cast as a JAWT_Win32DrawingSurfaceInfo* . And, of course, the actual drawing operations would need to be changed to those appropriate for the Microsoft Windows platform.

Summary

The ability to draw directly into a Java Canvas from a native code library is extremely useful for developers planning to migrate a legacy software system to Java, especially one that includes a high-performance rendering engine. It makes it much easier to migrate in stages, leaving performance-sensitive rendering code alone, while other less-sensitive portions of code are converted to Java. The result can be a modern Java-centric application, providing the benefit of portability and development efficiency, but one that does not sacrifice an investment in performance of a key piece of native code.

References

The definitive reference to the Java Native Interface is The Java Native Interface: Programmer's Guide and Specification by Sheng Liang. This book was published in June 1999 by Addison-Wesley. The ISBN is 0-201-32577-2.

Appendix

Header Files for jawt.h and jawt_md.h

jawt.h

#ifndef JAVASOFT_JAWT_H #define JAVASOFT_JAWT_H

#include "jni.h"

#ifdef __cplusplus extern "C" { #endif

/*

/*



/*

struct jawt_DrawingSurface;

/*

#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;

/*

} JAWT;

/*

#define JAWT_VERSION_1_3 0x00010003

#ifdef __cplusplus } /* extern "C" */ #endif

#endif /* !JAVASOFT_JAWT_H */

jawt_md.h (Solaris/X11 operating environment version)

#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

/*

#ifdef __cplusplus } #endif

#endif /* !JAVASOFT_JAWT_MD_H */

jawt_md.h (Microsoft Windows version)

#ifndef JAVASOFT_JAWT_MD_H #define JAVASOFT_JAWT_MD_H

#include <windows.h> #include "jawt.h"

#ifdef __cplusplus extern "C" { #endif

/*

#ifdef __cplusplus } #endif

#endif /* !JAVASOFT_JAWT_MD_H */