I am trying to create an image from a binary file. The file contains a 32x32 icon and its corresponding 16 colors palette.
Specification
The icon is 512 bytes long. The icon is separated in 4x4 tiles. Each tile is 4x8 bytes.
Here's the 4x8 arrangement of bytes for a single tile:
B B B B
B B B B B B B B B B B B B B B B B B B B B B B B B B B B
Here's the bits expanded from the above bytes:
11110000 11110000 11110000 11110000
00001111 00001111 00001111 00001111 11110000 11110000 11110000 11110000 00001111 00001111 00001111 00001111 11110000 11110000 11110000 11110000 00001111 00001111 00001111 00001111 11110000 11110000 11110000 11110000 00001111 00001111 00001111 00001111
Breaking each byte into four pixels each gives the following tile:
1111 0000 1111 0000 1111 0000 1111 0000
0000 1111 0000 1111 0000 1111 0000 1111 1111 0000 1111 0000 1111 0000 1111 0000 0000 1111 0000 1111 0000 1111 0000 1111 1111 0000 1111 0000 1111 0000 1111 0000 0000 1111 0000 1111 0000 1111 0000 1111 1111 0000 1111 0000 1111 0000 1111 0000 0000 1111 0000 1111 0000 1111 0000 1111
Each 4 bit is the index of the color in the palette.
The color palette is 32 bytes long and contains 16 colors. Each color is 16bits (5 for each component while the last is unused).
Problem - Step 1
I have managed to parse the image data into an array of 512 bytes and the color palette into an array of 32 bytes. But I am not really sure how to continue from hereon.
I read all the image data bytes into a BitSet, but I am not sure if this is even useful.
Also, I don't know how to construct a color from two bytes.
Any help/suggestions/comments?
Thank you.
Problem - Step 2
So with your help I've created an IndexColorModel out of the the color palette as follows:
int[] colors = new int[16*3];
for (int i = 0; i < 16; i++) {
byte b1 = this.palette[i]; // byte 1: 5 bits of R and 3 bits of G
byte b2 = this.palette[i+1]; // byte 2: 2 bits of G and 5 bits of B and 0.
// colors are encoded in inverse order
colors[i+2] = (b2 & 0x3E) >>> // red
colors[i+1] = ((b1 & 0x07) << 2) | ((b2 & 0xC0) >> 6); // green
colors[i] = (b1 & 0xF8) >>> 3; // blue
}
IndexColorModel cm = new IndexColorModel(5, 16*3, colors, 0, false, 0, DataBuffer.TYPE_BYTE);
Now I need to create a BufferedImage from the array of bytes I have read from the binary file using the above IndexColorModel.
So far I have this:
DataBuffer buffer = new DataBufferByte(this.titleData, 32*32);
int pixelStride = 4; //assuming r, g, b, skip, r, g, b, skip...
int scanlineStride = 4*32; //no extra paddi开发者_高级运维ng
int[] bandOffsets = {0, 1, 2}; //r, g, b
WritableRaster raster = Raster.createInterleavedRaster(buffer, 32, 32, scanlineStride, pixelStride, bandOffsets, null);
boolean isAlphaPremultiplied = false;
BufferedImage bim = new BufferedImage(cm, raster, isAlphaPremultiplied, null);
Taken from here.
this.titleData is an array of 512 bytes that where read from the binary file.
The above code throws the following exception:
Caused by: java.awt.image.RasterFormatException: Data array too small (should be 4094 )
Any help? Once again, thank you very much.
The javax.imageio.ImageIO
class and its relatives are what you need. Essentially, the javax.imageio
package.
I've forgotten the details, but the framework even allows you to define a custom color encoding (i.e. number of bits per color and their order) when mapping between an in-memory structure and the output file.
Update
Still waiting on your answer whether file I/O or just a local image. Meanwhile...
If you look at class java.awt.image.BufferedImage
, you'll see constants for encoding some of the usual bit-to-pixel mappings, constructors for creating an image from a raster, methods for setting up a color model, for setting up a raster and so on.
You'll probably also want to look at a decent tutorial on this. I'll go look for one...
To answer the question of how to map two bytes into a color, look at java.awt.image.ColorModel
and java.awt.image.ComponentColorModel
. I think the second one is the one you want.
I fought my way through this a while ago (using the java.awt.image
stuff) but now I'm appalled to find I've forgotten too much to be of much help.
Just in case you have too much trouble with the Java API approach, I'd like to answer your question to Michael Brewer-Davis:
Given that a color is 16bits with 5 bits for each components (the last one is unused), could you show me how to construct the color. Have in mind that I read the data from binary byte-by-byte. Which means that I parse the whole color palette in an array of 32 bytes. In other words, I need first to stick together two bytes for each color. Thank you.
byte b1 = 0x??; // byte 1: 5 bits of R and 3 bits of G
byte b2 = 0x??; // byte 2: 2 bits of G and 5 bits of B and 0.
int r = (b1 & 0xF8) >>> 3;
int g = ((b1 & 0x07) << 2) | ((b2 & 0xC0) >>> 6);
int b = (b2 & 0x3E) >>> 1;
In each case, you're masking the bits of interest in the byte by ANDing with a number having only those bits set (represented in hex because that's easier to work with), and then shifting the bits around so they'll end up right-aligned within the result int.
The rest is easy:
Color c = new Color(r, g, b);
How are you encoding colours? You can use the Color
class to produce color objects from their components.
You can then draw a bunch of 1x1 rectangles of the right colour on a Graphics object in order to build up your image.
If the image format is well known, ImageIO classes might work, or there may be a service provider that you could find to parse it from within ImageIO. If it's a custom format, you'll need to roll your own.
First you need to resolve your color model, i.e. determine how the 16-bit colors map to real world colors. Most likely this is 15-bit RGB (555) or 16-bit RGB (565), but anything's possible (e.g., 4444 ARGB, with some of the bytes used for alpha/transparency).
Once you've done that, the simplest thing to do is to create and fill in a BufferedImage
based on the tiling structure that you've given.
Something like:
BufferedImage image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
byte[] imageData;
for (int i = 0; i < imageData.length; ++i) {
int x = // figure out x based on tiling
int y = // figure out y based on tiling
byte highBits = (imageData[i] >> 4) & 0xF;
Color c = getColor(highBits); // translate bits into RGB color model
image.setRGB(x, y, c.getRGB());
x = // figure out for second bits, probably x+1?
y = // figure out for second bits, probably unchanged?
byte lowBits = imageData[i] & 0xF;
c = getColor(lowBits);
image.setRGB(x, y, c.getRGB());
}
You could also define your own ColorModel and define your image that way, particularly if this is a file format you intend to use widely.
精彩评论