I have a requirement to create a BMP image (black & white) from the hex string created through Excel Macro. I have next to zero experience working on this and need some help. Below is the Hex String, output image example of the hex, and little code snippet that I am trying :
String:
001E00FE07FE3FE03C003C003F801FFE03FE007E3E003FE03FFC0FFE0E3E0FFE3FFC3FE03E0000000FF83FFC3FFE300630063006380E180C00003E003FE03FFC0FFE0E3E0FFE3FFC3FE03E00000000003FFE3FFE3FFE01F807F01FC03FFE3FFE3FFE0000000E000E000E3FFE3FFE3FFE000E000E000E0000
Image to be created of the above string:
Code Snippet :
public void createImageFromHex() throws IOException {
String hex="001E00FE07FE3FE03C003C003F801FFE03FE007E3E003FE03FFC0FFE0E3E0FFE3FFC3FE03E0000000FF83FFC3FFE300630063006380E180C00003E003FE03FFC0FFE0E3E0FFE3FFC3FE03E00000000003FFE3FFE3FFE01F807F01FC03FFE3FFE3FFE0000000E000E000E3FFE3FFE3FFE000E000E000E0000";
byte[] imageInByte= ByteString.decodeHex(hex).toByteArray();
for (byte b : imageInByte) {
System.out.println("Byte : " + b);
}
InputStream in = new ByteArrayInputStream(imageInByte);
Font font = new Font("Arial", Font.PLAIN, 12);
BufferedImage img = new BufferedImage(1, 1, BufferedImage.BITMASK);
Graphics2D g2d = img.createGraphics();
FontMetrics fm = g2d.getFontMetrics(font);
g2d.dispose();
int width = fm.stringWidth(hex);
int height = fm.getHeight();
img = new BufferedImage(width, height, BufferedImage.BITMASK);
g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.BLACK);
g2d.setFont(font);
g2d.drawString(hex, 0, fm.getAscent());
g2d.dispose();
try {
ImageIO.write(img, "bmp", new File("Hex.bmp"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
The clue here is the format of the hex/binary data. You need to know this from some kind of specification, from the device/software/service that produces the hex string. From the data and sample image, I believe @tgdavies has correctly reverse-engineered it.
Each pixel is one bit. The white pixels are stored as 0, black as 1. Each column of pixels is stored as two bytes. Each column is stored bottom-up.
Assuming that format is correct, it's quite trivial to write code to convert it:
public void createImageFromHex() throws IOException {
// Assumption: hex contains a bitmap format, where
// * each pair of bytes is one *column*
// * each column is stored "bottom-up" (LSB is bottom pixel, MSB is top pixel)
String hex = "001E00FE07FE3FE03C003C003F801FFE03FE007E3E003FE03FFC0FFE0E3E0FFE3FFC3FE03E0000000FF83FFC3FFE300630063006380E180C00003E003FE03FFC0FFE0E3E0FFE3FFC3FE03E00000000003FFE3FFE3FFE01F807F01FC03FFE3FFE3FFE0000000E000E000E3FFE3FFE3FFE000E000E000E0000";
// (result is 120 bytes)
byte[] imageInByte = ByteString.decodeHex(hex).toByteArray();
int height = 16; // 16 according to sample
int width = imageInByte.length / 2; // 60 according to the sample, but as we know each column is 16 bits or 2 bytes, we can calculate it
// Create a BufferedImage of correct type: For a bitmap this is TYPE_BYTE_BINARY
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
// Loop over pixels and insert black or white
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int pos = height * x + y;
int bytePos = pos / 8;
int bitPos = 7 - (pos % 8);
byte value = imageInByte[bytePos];
int bit = (value >> bitPos) & 1;
// Each pixel is either all white (0) or all black (1)
int rgb = bit != 0 ? 0x000000 : 0xFFFFFF;
img.setRGB(x, height - 1 - y, rgb);
}
}
// Write to BMP format
File output = new File("Hex.bmp");
if (!ImageIO.write(img, "bmp", output)) {
System.err.printf("Could not write %s in BMP format...%n", output.getAbsolutePath());
}
}
Result file: