tags:

views:

459

answers:

4

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 padding   
  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.

A: 

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.

Anon.
Well, unfortunately, the specification I have doesn't exactly explain how colors are encoded, but after some research I think it's 5 bits for each color (the last one is not used). So probably it's RRRRRGGGGGBBBBBU. How can I construct a color from this...? :S
pek
Use bitwise operations to get the bits of each colour into separate ints, then use `new Color(r, g, b)`.
Anon.
Sorry if I'm asking too much, but I have two bytes for one color. How do I combine this an extract the ints? I'm not very familiar with bitwise operations, if you could provide some code I would be grateful.
pek
While I'm still a bit foggy on the details of my own answer, I think performance will be horrible if you try to do this on a pixel-by-pixel basis. There is functionality in the java API to do this for arrays of data at a time, with explicit specification of bit counts and color order.
Carl Smotricz
I really hope this is the case. ;P
pek
+2  A: 
Carl Smotricz
Sorry, didn't see your question. Indeed, I've tried figuring out this color model but couldn't find a good tutorial. I am currently looking at this: http://java.sun.com/developer/technicalArticles/GUI/java2d/java2dpart2.html
pek
That's not bad, it looks similar to what I used when I did this, but only once, about 2 years ago.
Carl Smotricz
ComponentColorModel? All this time I was trying to figure out IndexColorModel because the description sounded a lot like what I needed... :S
pek
Found this tutorial, about 4 pages, nice and detailed: http://javaboutique.internet.com/tutorials/rasters/
Carl Smotricz
Oops, I missed where you mention that you have a palette. IndexColorModel it is, you're correct.
Carl Smotricz
Oh, OK. Thank you. I'm reading it right now.
pek
I've added an answer on how to generate R, G, B from 2 bytes, just in case you run into a wall. I'm afraid I need to get to bed.
Carl Smotricz
Well, you have provided enough for me to get me started so thank you very much and good night. ;)
pek
A: 

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.

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.
pek
+1  A: 

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);
Carl Smotricz
Awesome! Thanks. ;)
pek