当图像大小超过255像素时,如何在DIB header中设置BMP文件的宽度和高度?

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

我正在使用 Java 创建简单的 BMP 图像。不幸的是,当宽度或高度的大小超过 255 的小数值(十六进制的 FF 值)时,我不知道如何将宽度和高度大小信息存储到 DIB header 中。要创建小尺寸图像 (255x255),我只需输入

宽度:FF 00 00 00,

身高:FF 00 00 00.

我可以在一个地方输入的最大值是十六进制的 FF(小数为 255)。如何在 00 00 00 00 这四个地方输入大于 255(十六进制 FF)的值,以创建任何大尺寸图像,例如 513x513 像素?不知何故我不得不使用其他零,但我无法在任何地方找到任何像样的解释。我应该如何在 Java 代码中实现这个原则?如果有任何帮助,我将不胜感激。

这是我的 Java 代码的一部分,用于将尺寸值输入 DIB header 以创建不大于 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);

这是我的完整 Java 代码:

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();
        }
    }
}

尝试移动值以写入更高的值

public void WriteDWORD(int i)
{
    writer.write((byte)(i >> 24));
    writer.write((byte)(i >> 16));
    writer.write((byte)(i >> 8));
    writer.write((byte)i);
}

也可以是小端(向后) 查看有关字节顺序的维基百科 http://en.wikipedia.org/wiki/Endianness

public void WriteDWORDLittleEndian(int i)
{
    writer.write((byte)i);
    writer.write((byte)(i >> 8));
    writer.write((byte)(i >> 16));
    writer.write((byte)(i >> 24));
}

还要检查您的编写器是否有像 .writeLong.writeInt.writeInteger 这样的命令 所以你不必自己做一个。

要获取这些命令,您必须执行 http://docs.oracle.com/javase/7/docs/api/java/io/DataOutputStream.html

import java.io.DataOutputStream;
DataOutputStream dos = new DataOutputStream(writer);
dos.writeInt(1234567);
dos.close();

我找到了解决办法。我将只讲述如何存储宽度大小信息,因为高度信息以相同的方式存储。我们有 4 个字节来存储宽度信息,如下所示:

00 00 00 00

这里存储的所有信息都必须是十六进制的,因此如果我们想存储 255 宽度的信息,我们将 FF 十六进制值(十进制中的 255)放在前两个零中,如下所示:

FF 00 00 00

如果我们要存储更大的宽度,比如256,我们不能简单地在前两个零的位置输入100值(十六进制为256),如下所示:

100 00 00 00 这是非常错误的而且不合逻辑

相反,我们还必须使用第二个两个零,如下所示:

00 01 00 00

如果我们想存储 257 或 258 或 259 个宽度值,我们这样做:

01 01 00 00 257

02 01 00 00 258

03 01 00 00 259

请注意,第二个 2 个零仍为 01,并且仅更改前两个值。当前两个零低于 FF 时会发生这种情况,当值变得高于 FF 时,我们必须将第二个两个值更改为 02 并再次开始将值添加到前两个零。例如,如果我们想存储到 DIB header 512 or 513 or 1024 or 1025 我们必须这样做:

00 02 00 00 512

01 02 00 00 513

00 04 00 00 1024

01 04 00 00 1025

等等。我可能是错的,但我相信这个系统叫做 little-endian 系统。正如已经提到的 SSpoke,可以找到有关这些字节顺序的更多信息 here。同样如 SSpoke 所述,要在 Java 或其他语言中实现此原则,您必须使用按位运算 >> 来移动值。下面是实现这个原则的代码:

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

让我们使用这行代码 V_GRID >> 8 % 256 并假设 V_GRID 值为 257。 为了简单起见,为了理解这条线是如何工作的,我将讲述我是如何理解它的,但计算机的工作方式可能会有所不同。 257 将转换为二进制数 100000001,然后按位运算 >> 将此 100000001 二进制数向右移动 8 个值,我们得到 1(100000001 >> 8 = 1)。最后将 1 转换为十进制值(在本例中为 1),我们得到提醒(也是 1) 然后我们将这个值(1)放入 BMP DIB header。可以找到有关按位运算的体面解释 here。这是我最终的完整 Java 代码:

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();
        }
    }
}