FAQ (Frequently Asked Questions) for Sun Java developers on using WSQ image library
In Sun Java it is not possible to directly call a C function in a DLL, however you can do that using Java Native Interface (JNI) wrapper DLL.
Java calls Windows DLL in the following way:
Java program <-> JNI wrapper DLL <-> Windows DLL.
Such a wrapper interface is provided in DLL file "WSQ_library_jniexport.dll".
You can rewrite JNI source code to better suit your application needs.
JNI wrapper source code for "WSQ image library (for Windows 32-bit)" is available here:
To download GNU C 8.1, MS Visual C++ 2008 and Embarcadero C++ Builder 10 Seattle JNI source code project click here
JNI wrapper source code for "WSQ image library (for Windows 64-bit)" is available here:
To download GNU C 8.1, MS Visual C++ 2008 and Embarcadero C++ Builder 10 Seattle JNI source code project click here
JNI wrapper source code for "WSQ image library (for Linux 32-bit and Linux 64-bit)" is available here:
To download GNU C Compiler GCC 4.2.3 JNI source code project click here
JNI wrapper source code for "WSQ image library (for Intel-based macOS 64-bit)" is available here:
To download GNU C Compiler GCC 4.2.3 JNI source code project click here
The Java Native Interface (JNI) is a programming framework that allows Java code running in the Java virtual machine (VM) to call and be called by native applications (programs specific to a hardware and operating system platform) and libraries written in other languages, such as C, C++ and assembly.
The JNI is used to write native methods to handle situations when an application cannot be written entirely in the Java programming language such as when the standard Java class library does not support the platform-dependent features or program library. It is also used to modify an existing application, written in another programming language, to be accessible to Java applications. Many of the standard library classes depend on the JNI to provide functionality to the developer and the user, e.g. I/O file reading and sound capabilities. Including performance- and platform-sensitive API implementations in the standard library allows all Java applications to access this functionality in a safe and platform-independent manner. Before resorting to using the JNI developers should make sure the functionality is not already provided in the standard libraries.
The JNI framework lets a native method utilize Java objects in the same way that Java code uses these objects. A native method can create Java objects and then inspect and use these objects to perform its tasks. A native method can also inspect and use objects created by Java application code.
JNI is sometimes referred to as the "escape valve" for Java developers because it allows them to add functionality to their Java Application that the Java API can't provide. It is also used to interface with code written in other languages, like C++. Or, it is used for time-critical calculations or operations like solving complicated mathematical equations, since native code is much faster than JVM code.
JNI really consists of two parts - the ability to implement Java methods using native code, and the "Invocation API," which allows a native application to embed the Java VM. We're going to focus on the first approach, so our programs will start as Java programs which use native code to implement some methods.
Native methods can be static or non-static.
We will illustrate the concept with "Hello world" program example.
Since our Hello World method will be very procedural, we'll go ahead and make it static.
The general steps required to implement our example will be:
- Create a Java class with a native method, and compile it,
- Use the javah tool to generate a C header file for the class,
- Write the C code implementing the method, compile it, and link it into a DLL,
- Insert code into our Java program telling it to load the DLL,
- Enable our Java program to find the DLL,
- Execute our Java program,
- Read Hello World in the console window.
Java function declaration example:
|
private static native void writeHelloWorldToStdout(); |
Example of declaration in C header file (*.h):
|
JNIEXPORT void JNICALL Java_example_jni_HelloWorld_writeHelloWorldToStdout(JNIEnv *, jclass); |
Command for C header file (*.h) generation:
|
javah -classpath [wherever you compiled HelloWorld] -o HelloWorld.h example.jni.HelloWorld |
Java code which loads the library into the JVM:
|
public class HelloWorld {
private static native void writeHelloWorldToStdout();
public static void main(String[] args) {
System.loadLibrary("HelloWorld");
writeHelloWorldToStdout();
}
} |
JNIEXPORT and JNICALL are function calling conventions and they are defined in the file "jni_md.h" as:
|
#define JNIEXPORT __declspec(dllexport)
#define JNICALL __stdcall |
http://java.sun.com/docs/books/jni/html/pitfalls.html
Cached
http://www.particle.kth.se/~lindsey/JavaCourse/Book/Part3/Chapter22/cppCode.html
Cached
Convert HBITMAP to BITMAP format using WIN32 API. Get Bitmap data as Width, Height, RGB Data (as integer array). Take into account that BITMAP stores pixels from left-updown but java stores from left-downup. So you should reorder pixels (integer array) before you store it in BufferedImage with method:
|
public void setRGB(int startX,
int startY,
int w,
int h,
int[] rgbArray,
int offset,
int scansize); |
Java code (using JNI):
|
int width = 100;
int height = 100;
int[] ints = new int[width * height]; // the GetDIBits destination buffer
int[] bandMasks = new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff };
DirectColorModel colorModel = new DirectColorModel(32, bandMasks[0], bandMasks[1], bandMasks[2]);
DataBuffer dataBuffer = new DataBufferInt(ints, width * height);
WritableRaster writableRaster = Raster.createPackedRaster(dataBuffer, width, height, width, bandMasks, null);
BufferedImage image = new BufferedImage(colorModel, writableRaster, true, null);
Win32.GetDeviceBitmap(ints, hdc, x, y, width, height); // call to GetDIBits for the given HDC
|
Function in C:
|
JNIEXPORT void JNICALL Java_Win32_GetDeviceBitmap
(JNIEnv *env, jclass c, jintArray image, jint hdc, jint x, jint y, jint width, jint height) {
HDC memdc = CreateCompatibleDC((HDC)hdc);
HBITMAP bmp = CreateCompatibleBitmap((HDC)hdc, width, height);
HBITMAP hprevbitmap = (HBITMAP) SelectObject(memdc, bmp);
BitBlt(memdc, 0, 0, width, height, (HDC)hdc, x, y, SRCCOPY);
SelectObject(memdc, hprevbitmap);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = width;
info.bmiHeader.biHeight = -height;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biSizeImage = 0;
info.bmiHeader.biClrUsed = 0;
jboolean isCopy = JNI_FALSE;
jint *ints = (jint *)env->GetPrimitiveArrayCritical(image, &isCopy);
if (isCopy) {
printf("GetDeviceBitmap() - WARNING, array is a copy!\n");
}
GetDIBits((HDC)hdc, bmp, 0, height, ints, &info, DIB_RGB_COLORS);
env->ReleasePrimitiveArrayCritical(image, ints, JNI_ABORT);
DeleteObject(bmp);
DeleteDC(memdc);
}
|
It can be better to use the following JNI-AWT method to draw the image into the "Graphics2D" of a BufferedImage:
|
/*
* 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 Win32 and Solaris support the existing structures in jawt_md.h.
*
*******************
* EXAMPLE OF USAGE:
*******************
*
* In Win32, 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
awt.version = JAWT_VERSION_1_3;
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);
}
|
More about DirectColorModel:
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/image/DirectColorModel.html
Cached
More about Image:
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Image.html
Cached
More about Raster:
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/image/Raster.html
Cached
More about DataBuffer:
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/image/DataBuffer.html
Cached
More about DataBufferInt:
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/image/DataBufferInt.html
Cached
File choosers provide a GUI for navigating the file system, and then either choosing a file or directory from a list or entering the name of a file or directory. To display a file chooser, you usually use the JFileChooser API to show a modal dialog containing the file chooser. Another way to present a file chooser is to add an instance of JFileChooser to a container.
The JFileChooser API makes it easy to bring up open and save dialogs. The look and feel determines what these standard dialogs look like and how they differ. In the Java look and feel, the save dialog looks the same as the open dialog, except for the title on the dialog's window and the text on the button that approves the operation.
Filtering the List of Files
By default, a file chooser displays all of the files and directories that it detects, except hidden files. A program can apply one or more file filters to a file chooser so that the chooser shows only some files. The file chooser calls the filter's accept method for each file to determine whether it should be displayed. A file filter accepts or rejects a file based on some criteria such as file type, size, ownership, and so on. Filters affect the list of files displayed by the file chooser. The user can enter the name of any file even if it's not displayed.
JFileChooser supports three different kinds of filtering. The filters are checked in the order listed here. For example, an application-controlled filter sees only those files accepted by the built-in filtering.
[1] Built-in filtering
Filtering is set up through specific method calls on a file chooser. Currently the only built-in filter available is for hidden files, such as those that begin with period (.) on UNIX systems. By default, hidden files are not shown. Call setFileHidingEnabled(false) to show hidden files.
[2] Application-controlled filtering
The application determines which files are shown. Create a custom subclass of FileFilter, instantiate it, and use the instance as an argument to setFileFilter. The file chooser shows only those files that the filter accepts.
[3] User-choosable filtering
The file chooser GUI provides a list of filters that the user can choose from. When the user chooses a filter, the file chooser shows only those file accepted by that filter. FileChooserDemo2 adds a custom file filter to the list of user-choosable filters:
|
fc.addChoosableFileFilter(new ImageFilter()); |
By default, the list of user-choosable filters includes the Accept All filter, which lets the user see all non-hidden files. This example uses the following code to disable the Accept All filter:
|
fc.setAcceptAllFileFilterUsed(false); |
Our custom file filter is implemented in ImageFilter.java and is a subclass of FileFilter. The ImageFilter class implements the getDescription method to return "Just Images" - a string to put in the list of user-choosable filters. ImageFilter also implements the accept method so that it accepts all directories and any file that has a .png, .jpg, .jpeg, .gif, .tif, or .tiff filename extension.
|
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String extension = Utils.getExtension(f);
if (extension != null) {
if (extension.equals(Utils.tiff) ||
extension.equals(Utils.tif) ||
extension.equals(Utils.gif) ||
extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg) ||
extension.equals(Utils.png)) {
return true;
} else {
return false;
}
}
return false;
}
|
By accepting all directories, this filter allows the user to navigate around the file system. If the bold lines were omitted from this method, the user would be limited to the directory with which the chooser was initialized.
The preceding code sample uses the getExtension method and several string constants from Utils.java, shown here:
|
public class Utils {
public final static String jpeg = "jpeg";
public final static String jpg = "jpg";
public final static String gif = "gif";
public final static String tiff = "tiff";
public final static String tif = "tif";
public final static String png = "png";
/*
* Get the extension of a file.
*/
public static String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
}
|
Class FileFilter
Constructor Summary
| FileFilter()
|
Method Summary
| abstract boolean
| accept(File f) Whether the given file is accepted by this filter.
| abstract String
| getDescription() The description of this filter.
|
You can declare an inner class without naming it, called an anonymous class. Here's yet version of the Stack class, in this case using an anonymous class for its iterator:
|
public class Stack {
private ArrayList<Object> items;
//code for Stack's methods and constructors
//not shown
public Iterator<Object> iterator() {
return new Iterator<Object>() {
int currentItem = items.size() - 1;
public boolean hasNext() {
...
}
public ArrayList<Object> next() {
...
}
public void remove() {
...
}
};
}
}
|
|