The Source for Java Technology Collaboration
User: Password:



Start New Message Delete Post a Reply

Article: 
 Java Tech: Acquire Images with TWAIN and SANE, Part 2
Subject:  Support for 2 colors B/W..?
Date:  2005-05-04 14:04:34
From:  javajeff
Response to: Support for 2 colors B/W..?


Hello,

I've read somewhere that the two most popular BMP formats are 8-bit color and
24-bit color. This is why I chose not to support 1-bit color.

However, I've expanded jtwain.cpp to support 1-bit color. Below are the code
fragments that have been modified or are new in that source file.

The first code fragment belongs in the acquire() function. It simply changes
the validation test to include ii.BitsPerPixel != 1. The error message is
modified as well.


// If image is compressed or is not 1-bit color and not 8-bit color
// and not 24-bit color ...

if (ii.Compression != TWCP_NONE ||
ii.BitsPerPixel != 1 &&
ii.BitsPerPixel != 8 &&
ii.BitsPerPixel != 24)
{
// Cancel all transfers.

(*g_pDSM_Entry) (&g_AppID,
&srcID,
DG_CONTROL,
DAT_PENDINGXFERS,
MSG_RESET,
(TW_MEMREF) &pxfers);

// Throw exception upon return to Java and break out of event
// loop.

throwJTE (env, "Image compressed or not 1-bit/8-bit/24-bit "
"(acquire)");
break;
}


The second code fragment belongs in the acquire() function. It simply adds an
if test to determine if ii.BitsPerPixel contains 1. If so, a new function is
called to perform the image transfer.


if (ii.BitsPerPixel == 1)
image = xferDIB1toImage (lpbmih, env);
else
if (ii.BitsPerPixel == 8)
image = xferDIB8toImage (lpbmih, env);
else
image = xferDIB24toImage (lpbmih, env);


The third and final code fragment represents the beginning of the new image
transfer function. I haven't included all of the code because the remainder of
the function is identical to the other two image transfer functions. Don't
forget to supply a function protoype for this function at the beginning of the
source file -- where the function prototypes to the other two image transfer
functions are declared.


static jobject xferDIB1toImage (LPBITMAPINFOHEADER lpbmih, JNIEnv *env)
{
// Obtain the image's width and height -- both in pixels -- to pass to the
// MemoryImageSource constructor.

int width = lpbmih->biWidth;

int height = lpbmih->biHeight; // height < 0 if bitmap is top-down
if (height < 0)
height = -height;

// Create Java-based integer pixels array to pass to the MemoryImageSource
// constructor.

jintArray pixels = env->NewIntArray (width * height);
if (pixels == 0)
{
throwJTE (env, "Insufficient memory for pixels array "
"(xferDIB1toImage)");
return (jobject) 0;
}

// Populate the pixels array.

int *palette = (int *) lpbmih + sizeof(BITMAPINFOHEADER);

int numColors;

if (lpbmih->biClrUsed > 0)
numColors = lpbmih->biClrUsed;
else
numColors = 1 << lpbmih->biBitCount;

unsigned char *bitmap = (unsigned char *) lpbmih +
sizeof(BITMAPINFOHEADER) +
numColors * sizeof(RGBQUAD);

jboolean isCopy;
jint *pixelsArray = env->GetIntArrayElements (pixels, &isCopy);
if (pixelsArray == 0)
{
throwJTE (env, "Insufficient memory (xferDIB1toImage)");
return (jobject) 0;
}

// Convert width pixels to rowBytes bytes.

int rowBytes = width / 8;
if ((width % 8) != 0)
rowBytes++;

// Each row is a multiple of 4 bytes.

if (rowBytes % 4 != 0)
rowBytes += (4 - rowBytes % 4);

for (int row = 0; row < height; row++)
for (int col = 0; col < width; col++)
{
// Extract color information for pixel and build an equivalent
// Java pixel for storage in the Java-based integer array.

int byte = bitmap [rowBytes*row+col/8];
int masks [] = { 128, 64, 32, 16, 8, 4, 2, 1 };
int pixel = 0xff000000;
if ((byte & masks [col % 8]) != 0)
pixel |= 0xffffff;

// Store the pixel in the array at the appropriate index.

pixelsArray [width*(height-row-1)+col] = (jint) pixel;
}

if (isCopy == JNI_TRUE)
env->ReleaseIntArrayElements (pixels, pixelsArray, 0);

// Build the equivalent of the following Java code fragement:
//
// MemoryImageSource mis;
// mis = new MemoryImageSource (width, height, pixels, 0, width);
// Image im = Toolkit.getDefaultToolkit ().createImage (mis);


There is a curiosity about this function. It includes a palette declaration
but does not use palette anywhere. Why? When I started writing the function, I
was under the impression that the bitmap I was testing against would contain a
palette -- because biClrUsed contains 2 and biBitCount contain 1. My Windows
documentation indicated that this would be the case. However, the
documentation also referred to a packed bitmap that would not contain a
palette. As I discovered, the bitmap was packed and so there was no palette.
When you test these changes in your own environment, it might be that your
bitmap is not packed. If so, simply replace pixel |= 0xffffff with
pixel |= palette [1]. Go figure Windows out!

If you would like a copy of the complete jtwain.cpp file, please send an email
to myself at jeff@gatewest.net. I hope this is helpful to you.

Jeff

 Feed java.net RSS Feeds