javaimagebitmapbmpdensity-independent-pixel

How to set BMP file width and height in DIB header when image size exceeds 255 pixels?


I am creating simple BMP image with Java. Unfortunately I do not know how to store width and height size information into the DIB header, when the size of width or height exceeds 255 value in decimals (FF value in hex). To create small size image (255x255), I just enter

for width: FF 00 00 00,

for height: FF 00 00 00.

Maximum value that I can enter in one place is FF in hex (255 in decimals). How can I enter values larger than 255 (FF in hex) into these four places 00 00 00 00, in order to create any large size images, such as 513x513 pixels? Somehow I have to use other zeros, but I could not find any decent explanation anywhere. How should I implement this principle in Java code? I would be very grateful for any help.

This is part of my Java code that is used to enter size values to DIB header to create images not larger than 255x255.

writer = new FileOutputStream(new File(onName));
        // width
        writer.write(255);
        writer.write(0);
        writer.write(0);
        writer.write(0);

        // height
        writer.write(255);
        writer.write(0);
        writer.write(0);
        writer.write(0);

This is my full Java code:

package fractalTerrain.midpointDisplacement;
import java.io.*;
import java.util.Random;

// User: Fataho
// Date: 2015-01-10
public class MonochromeMidpointDisplacement {
    private static final String onName = "src\\fractalTerrain\\midpointDisplacement\\test2.bmp";
    private Random random = new Random();
    private static final int H_GRID = 256;
    private static final int V_GRID = 256;
    public static void main(String[] args) {
        MonochromeMidpointDisplacement midpointDisplacement = new MonochromeMidpointDisplacement();
        midpointDisplacement.go();

    }
    private void go(){
      //  fillMap(map, min, max);
        printMap();
    }

    public void printMap(){
        // 3.0 output to file
        // 3.1 Begin the file
        // 3.1.1 open output file

        FileOutputStream writer = null;
        try {
            writer = new FileOutputStream(new File(onName));

            // 3.1.2 copy the header
            // 3.1.2.1 magic number
            writer.write(66);
            writer.write(77);

            // 3.1.2.2 file size/unused space
            for (int i = 0; i < 8; i++){
                writer.write(0);
            }
            // 3.1.2.3 data offset
            writer.write(54);

            // 3.1.2.4 unused space
            for (int i = 0; i < 3; i++){
                writer.write(0);
            }

            // 3.1.2.5 header size
            writer.write(40);
            // 3.1.2.6 unused space
            for (int i = 0; i < 3; i++){
                writer.write(0);
            }


            // 3.1.2.7 file width (trickier)
            writer.write(255);
            writer.write(0);
            writer.write(0);
            writer.write(0);

            // 3.1.2.8 file height (trickier)
            writer.write(255);
            writer.write(0);
            writer.write(0);
            writer.write(0);

            // 3.1.2.9 color planes
            writer.write(1);
            writer.write(0);

            // 3.1.2.10 bit depth
            writer.write(24);

            // 3.1.2.11 the rest
            for (int i = 0; i < 25; i++){
                writer.write(0);
            }
                for(int g = 0; g < ((V_GRID)  * (H_GRID )); g++){
                    writer.write(255);
                    writer.write(0);
                    writer.write(0);
                }
            for (int k = 0; k < (H_GRID % 4); k++){
                writer.write(0);
            }

            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Solution

  • I found the solution. I will tell only how to store width size information, because height information is stored in same manner. We have 4 bytes to store width information as shown below:

    00 00 00 00

    All information stored here must be in hexadecimals, therefore if we want to store 255 width information we put FF hexadecimal value (255 in decimals) in first two zeros such like this:

    FF 00 00 00

    If we want to store larger width, such as 256, we cannot simply enter 100 value (256 in hexadecimals) in the place of first two zeros as shown below:

    100 00 00 00 THIS IS VERY WRONG AND NOT LOGICAL

    Instead we also have to use second two zeros such like this:

    00 01 00 00

    If we want to store 257 or 258 or 259 width values we do this:

    01 01 00 00 for 257

    02 01 00 00 for 258

    03 01 00 00 for 259

    notice that second 2 zeros remains 01 and changes only first two values. It will happen while first two zeros are below FF, when values become higher that FF, we have to change second two values to 02 and again start adding values to first two zeros. For instance, if we want to store into DIB header 512 or 513 or 1024 or 1025 we have to do this:

    00 02 00 00 for 512

    01 02 00 00 for 513

    00 04 00 00 for 1024

    01 04 00 00 for 1025

    and so on. I can be wrong, but I believe that this system is called little-endian system. As already mentioned SSpoke, more information about these endianness can be found here. Also as mentioned SSpoke, to implement this principle in Java or other language you have to shift values by using bitwise operation >>. Here is the code that implements this principle:

     writer.write(H_GRID % 256);
     writer.write(H_GRID >> 8 % 256);
     writer.write(H_GRID >> 16 % 256);
     writer.write(H_GRID >> 24 % 256);
    

    Let’s take this line of code V_GRID >> 8 % 256 and presume that V_GRID value is 257. For simplicity, to understand how this line works I will tell how I understand it, but it might be different how computer does it. 257 would is converted in to binary number 100000001, then bitwise operation >> shift this 100000001 binary number to the right by 8 values and we get 1 (100000001 >> 8 = 1). Finally 1 is converted to decimal value (in this case is 1) and we get the reminder (also 1) Then we put this value (1) in to BMP DIB header. A decent explanation about bitwise operations can be found here. Here is my final and full Java code:

    package fractalTerrain.midpointDisplacement;
    import java.io.*;
    import java.util.Random;
    
    public class MonochromeMidpointDisplacement {
        private static final String onName = "src\\fractalTerrain\\midpointDisplacement\\test2.bmp";
        private Random random = new Random();
        private static final int H_GRID = 257;
        private static final int V_GRID = H_GRID;
        public static void main(String[] args) {
            MonochromeMidpointDisplacement midpointDisplacement = new MonochromeMidpointDisplacement();
            midpointDisplacement.go();   
        }
        private void go(){
          //  fillMap(map, min, max);
            printMap();
        }
    
        public void printMap(){
            // 3.0 output to file
            // 3.1 Begin the file
            // 3.1.1 open output file
            FileOutputStream writer = null;
            try {
                writer = new FileOutputStream(new File(onName));
    
                // 3.1.2 copy the header
                // 3.1.2.1 magic number
                writer.write(66);
                writer.write(77);
    
                // 3.1.2.2 file size/unused space
                for (int i = 0; i < 8; i++){
                    writer.write(0);
                }
                // 3.1.2.3 data offset
                writer.write(54);
    
                // 3.1.2.4 unused space
                for (int i = 0; i < 3; i++){
                    writer.write(0);
                }
    
                // 3.1.2.5 header size
                writer.write(40);
                // 3.1.2.6 unused space
                for (int i = 0; i < 3; i++){
                    writer.write(0);
                }
    
                // 3.1.2.7 file width (trickier)
                writer.write(H_GRID % 256);
                writer.write(H_GRID >> 8 % 256);
                writer.write(H_GRID >> 16 % 256);
                writer.write(H_GRID >> 24 % 256);
    
                // 3.1.2.8 file height (trickier)
                writer.write(V_GRID % 256);
                writer.write(V_GRID >> 8 % 256);
                writer.write(V_GRID >> 16 % 256);
                writer.write(V_GRID >> 24 % 256);
    
                // 3.1.2.9 color planes
                writer.write(1);
                writer.write(0);
    
                // 3.1.2.10 bit depth
                writer.write(24);
    
                // 3.1.2.11 the rest
                for (int i = 0; i < 25; i++){
                    writer.write(0);
                }
    
                for(int i = 0; i < V_GRID; i++){
                    for(int g = 0; g < H_GRID; g++){
                        writer.write(255);
                        writer.write(0);
                        writer.write(0);
                    }
                    for (int k = 0; k < (H_GRID % 4); k++){
                        writer.write(0);
                    }
                }
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }