
December 26th, 2007 by Sam
J2ME is bare-bones and I recently realised that it doesn’t have any image encoders. Sure, you can create Image objects… but there is no way to persist them to disc or to send them over the network in a recognised format.
With the goal of creating a low-memory PNG encoder based on TinyLine GZIPInputStream, I wrote an uncompressed BMP encoder as a gentle introduction. I am posting it here (under the LGPL) incase it helps somebody out… I got tripped up by some file format details (including Endianness) so you don’t need to go through it.
Unfortunately, you won’t be able to load BMPs and display them without writing your own decoder! (J2ME only supports PNG)… but at least it allows you to communicate images with the outside world.
One of the biggest problems with J2ME is that there aren’t any great open source libraries out there. That’s one of Java’s greatest strengths. I intend to post a few of my J2ME utility classes here as I write them. I’m currently working on a hobby project which I intend to release here, and if it goes according to plan, I intend to reveal some bluetooth convenience classes.
http://www.gnu.org/licenses/
package thinktank.j2me;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.microedition.lcdui.Image;
@author
public final class BMPGenerator {
@param @return
@throws @see
public static byte[] encodeBMP(Image image) throws IOException {
int width = image.getWidth();
int height = image.getHeight();
int[] rgb = new int[height * width];
image.getRGB(rgb, 0, width, 0, 0, width, height);
return encodeBMP(rgb, width, height);
}
@param @param @param @return
@throws @see http://en.wikipedia.org/wiki/Windows_bitmap
public static byte[] encodeBMP(int[] rgb, int width, int height)
throws IOException {
int pad = (4 - (width % 4)) % 4;
int size = 14 + 40 + height * (pad + width * 3);
ByteArrayOutputStream bytes = new ByteArrayOutputStream(size);
DataOutputStream stream = new DataOutputStream(bytes);
stream.writeByte(0x42);
stream.writeByte(0x4D);
stream.writeInt(swapEndian(size));
stream.writeInt(0);
stream.writeInt(swapEndian(14 + 40));
stream.writeInt(swapEndian(40));
stream.writeInt(swapEndian(width));
stream.writeInt(swapEndian(height));
stream.writeShort(swapEndian((short) 1));
stream.writeShort(swapEndian((short) 24));
stream.writeInt(0);
stream.writeInt(0);
stream.writeInt(0);
stream.writeInt(0);
stream.writeInt(0);
stream.writeInt(0);
for (int j = height - 1; j >= 0; j--) {
for (int i = 0; i < width; i++) {
int val = rgb[i + width * j];
stream.writeByte(val & 0x000000FF);
stream.writeByte((val >>> 8 ) & 0x000000FF);
stream.writeByte((val >>> 16) & 0x000000FF);
}
for (int i = 0; i < pad; i++) {
stream.writeByte(0);
}
}
byte[] out = bytes.toByteArray();
bytes.close();
if (out.length != size)
throw new RuntimeException("bad math");
return out;
}
@param @return
private static int swapEndian(int value) {
int b1 = value & 0xff;
int b2 = (value >> 8 ) & 0xff;
int b3 = (value >> 16) & 0xff;
int b4 = (value >> 24) & 0xff;
return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
}
@param @return
private static short swapEndian(short value) {
int b1 = value & 0xff;
int b2 = (value >> 8 ) & 0xff;
return (short) (b1 << 8 | b2 << 0);
}
}
|