Search code examples
javajavax.imageio

Convert a PNG to Bitmap custom Hex String


This is opposite of this :

create BITMAP image from hex String Java

I need to take a PNG as input, and convert it into HEX String. The specification of how the Hex String is created is as below :

Each pixel column is read from bottom to top with four pixels encoded into a letter/number (A-F 0-9) according to the attached encoding scheme.

The bitmap code is a hex code that was developed for the hardware. Each number in the code controls a vertical section of four virtual pixels on the display. the first code letter controls the four pixels starting from the lower left corner and upwards. The second code letter controls the four next pixels above the first, the third the next four above the second, and the fourth letter the uppermost four pixels at the first column from the left. The fifth letter then controls the bottom four pixels of the second column, etc.

Here's what I have tried, which doesn't work :-(

public AppResponse<String> getBitmapCode(MultipartFile pngFile) throws IOException {

        BufferedImage img = ImageIO.read(pngFile.getInputStream());

        int height = img.getHeight();
        int width = img.getWidth();

        StringBuilder bitmapCode = new StringBuilder();

        // Process each column
        for (int x = 0; x < width; x++) {
            int pixelValue = 0;

            // Process each pixel in the column from bottom to top
            for (int y = height - 1; y >= 0; y--) {
                int rgb = img.getRGB(x, y);

                // Check if the pixel is closer to white or black using a threshold
                int bit = (getColorDistance(rgb, Color.WHITE) < getColorDistance(rgb, Color.BLACK)) ? 0 : 1;

                pixelValue = (pixelValue << 1) | bit;

                // Check if 4 pixels have been processed
                if ((height - 1 - y) % 4 == 0) {
                    // Convert the pixel value to the corresponding character
                    char encodedChar = encodePixelValue(pixelValue);
                    bitmapCode.append(encodedChar);
                    pixelValue = 0;
                }
            }
        }

        return AppResponse.responseOk("Success", bitmapCode.toString());
    }

    private static int getColorDistance(int rgb1, Color color2) {
        Color color1 = new Color(rgb1 & 0x00FFFFFF); // Remove alpha channel
        return (int) Math.sqrt(
                Math.pow(color1.getRed() - color2.getRed(), 2) +
                        Math.pow(color1.getGreen() - color2.getGreen(), 2) +
                        Math.pow(color1.getBlue() - color2.getBlue(), 2)
        );
    }

    private static char encodePixelValue(int pixelValue) {
        if (pixelValue >= 0 && pixelValue <= 9) {
            return (char) ('0' + pixelValue);
        } else if (pixelValue >= 10 && pixelValue <= 15) {
            return (char) ('A' + (pixelValue - 10));
        } else {
            throw new IllegalArgumentException("Invalid pixel value: " + pixelValue);
        }
    }

PNG is attached here : enter image description here

The image should generate hex like this :

000700070007FFFFFFFF00070007000700000000FFFFFFFFE1C7E1C7E1C7E1C7E00700000000183C787EF0C7E187E187E30F7E1E3C1C00000000000700070007FFFFFFFF00070007000700000000FFFFFFFF00000000FFFFFFFF00FC07E03F00FFFFFFFF000000003FFCFFFEE007E707E7077F3EFF3C0000

Output I get from my code : enter image description here


Solution

  • Your code is mostly correct. There are just two small errors.

    1. You have an off-by-one error in this part of your code:

      if ((height - 1 - y) % 4 == 0) {
          // Convert the pixel value to the corresponding character
          char encodedChar = encodePixelValue(pixelValue);
          bitmapCode.append(encodedChar);
          pixelValue = 0;
      }
      

      Change the condition to be (height - y) % 4 == 0).

    2. You need to set the bit variable to 1 when the color is closer to white than black, not 0. In other words, change condition ? 0 : 1 to condition ? 1 : 0.