jimp-dev / jimp

An image processing library written entirely in JavaScript for Node, with zero external or native dependencies.
http://jimp-dev.github.io/jimp/
MIT License
13.91k stars 762 forks source link

[Bug] Weird BMP load bug #802

Open elderapo opened 4 years ago

elderapo commented 4 years ago

Expected Behavior

The copied bmp file should be exactly the same as the original one.

Current Behavior

For some reason for this bmp file content gets moved/shifted. It does not happen for all the bmp files.

Failure Information (for bugs)

Steps to Reproduce

I've created minial reproducible repo:

git git@github.com:elderapo/jimp-bmp-bug.git
cd ./jimp-bmp-bug
yarn
yarn start

Screenshots from.bmp to.bmp

Context

Failure Logs

The loading system for bmp files seems to be broken. Exact same thing happens when reading bmp file from disk, buffer. Even when file is saved to a different format it still gets shifted.

cymplecy commented 4 years ago

I've been trying to run down an issue with bmp-js (which I believe jimp uses to read bmp files). AFAICT bmp-js does not use the offset value to the actual bitmap data contained in the header info of a bmp file. It just assumes that the bitmap follows on from the header.

I discovered this as I found that bmp files saved from GIMP have extra information in them that bmp files saved from both MSPaint and paint.net don't put in (see https://stackoverflow.com/questions/30505928/gimp-how-to-export-an-image-to-bmp-without-the-color-space-information-using-a for some information on this extra information )

Anyway - I ended up here and tried your from.bmp file out - and it has extra bytes between the header and the actual bitmap as well and I can replicate your fault in my code.

If I open from.bmp in Win MSPaint.exe and resave it as frompaint.bmp - the extra data is removed and reading the file works perfectly in jimp

I don't know how to resolve the issue since bmp-js doesn't seem to be maintained anymore

cymplecy commented 4 years ago

FTAOD - I'm confidant I can fix the code (at least for 24bit bmp files) but I don't know how to get any code changes back up to jimp

hipstersmoothie commented 4 years ago

I have a fork of bmp-ts I plan to integrate at some point. If you can find and fix the bug there I'll merge the changes into jimp

https://github.com/hipstersmoothie/bmp-ts

cymplecy commented 4 years ago

Looking at your .ts code, it seems to check the header size and make appropriate adjustments so its prob already fixed.

elderapo commented 4 years ago

Can confirm. This bug is no longer the case after switching to bmp-ts.

elderapo commented 4 years ago

Is there anything I can do to help get this merged?

sxwei123 commented 1 year ago

Any update on this issue? It is still there in the lastet version (0.22.8).

yingshaoxo commented 2 months ago

The GIMP wrote bugs, but I can't registe a gimp gitlab account to open an issue, can anyone do it for me and all those people?

They website is: https://gitlab.gnome.org/GNOME/gimp/-/issues


My bug example is two picture and a python function to read bmp file: dnf hero2_simplified_quick_mode


If you convert the first original image from png to bmp by using GIMP, you get a broken image.

I read that bmp file by using following python code:

#pybmp/__init__.py

from struct import unpack

class BMP:
    # author: rocketeerLi, https://blog.csdn.net/rocketeerLi/article/details/84929516
    def __init__(self, filePath) :
        file = open(filePath, "rb")
        # 读取 bmp 文件的文件头    14 字节
        self.bfType = unpack("<h", file.read(2))[0]       # 0x4d42 对应BM 表示这是Windows支持的位图格式
        self.bfSize = unpack("<i", file.read(4))[0]       # 位图文件大小
        self.bfReserved1 = unpack("<h", file.read(2))[0]  # 保留字段 必须设为 0 
        self.bfReserved2 = unpack("<h", file.read(2))[0]  # 保留字段 必须设为 0 
        self.bfOffBits = unpack("<i", file.read(4))[0]    # 偏移量 从文件头到位图数据需偏移多少字节(位图信息头、调色板长度等不是固定的,这时就需要这个参数了)
        # 读取 bmp 文件的位图信息头 40 字节
        self.biSize = unpack("<i", file.read(4))[0]       # 所需要的字节数
        self.biWidth = unpack("<i", file.read(4))[0]      # 图像的宽度 单位 像素
        self.biHeight = unpack("<i", file.read(4))[0]     # 图像的高度 单位 像素
        self.biPlanes = unpack("<h", file.read(2))[0]     # 说明颜色平面数 总设为 1
        self.biBitCount = unpack("<h", file.read(2))[0]   # 说明比特数

        self.biCompression = unpack("<i", file.read(4))[0]  # 图像压缩的数据类型
        self.biSizeImage = unpack("<i", file.read(4))[0]    # 图像大小
        self.biXPelsPerMeter = unpack("<i", file.read(4))[0]# 水平分辨率
        self.biYPelsPerMeter = unpack("<i", file.read(4))[0]# 垂直分辨率
        self.biClrUsed = unpack("<i", file.read(4))[0]      # 实际使用的彩色表中的颜色索引数
        self.biClrImportant = unpack("<i", file.read(4))[0] # 对图像显示有重要影响的颜色索引的数目
        self.bmp_data = []

        if self.biBitCount != 24 :
            raise Exception("(we need 24bit rgb bmp) 输入的图片比特值为 :" + str(self.biBitCount) + "\t 与程序不匹配")

        for height in range(self.biHeight) :
            bmp_data_row = []
            # 四字节填充位检测
            count = 0
            for width in range(self.biWidth) :
                bmp_data_row.append([unpack("<B", file.read(1))[0], unpack("<B", file.read(1))[0], unpack("<B", file.read(1))[0]])
                count = count + 3
            # bmp 四字节对齐原则
            while count % 4 != 0 :
                file.read(1)
                count = count + 1
            self.bmp_data.append(bmp_data_row)
        self.bmp_data.reverse()
        file.close()

        self.rgb_data = [None] * self.biHeight
        for row in range(self.biHeight) :
            one_row = [None] * self.biWidth
            for col in range(self.biWidth) :
                b = self.bmp_data[row][col][0]
                g = self.bmp_data[row][col][1]
                r = self.bmp_data[row][col][2]
                one_row[col] = [r,g,b,255]
            self.rgb_data[row] = one_row

def read_bmp_from_file(path):
    """
    return (height, width, raw_data)

    The newest GIMP or online png to bmp converter, they have used a bug version of bmp c++ library, so the bmp image you get from those sources will be broken in columns. If you use oldest ffmpeg to convert mp4 to bmp images, it would work fine.
    """
    img = BMP(path)

    height, width = img.biHeight, img.biWidth
    data = img.rgb_data

    return height, width, data

__all__ = [
    "BMP",
    "read_bmp_from_file",
]
yingshaoxo commented 2 months ago

According to an issue, you can also solve this bug by using:

The issue arises when an image is exported as BMP using GIMP and color space information is written to it.

To avoid this issue, select "Do not write color space information" under "Compatibility Options" when exporting as BMP.

https://github.com/jimp-dev/jimp/issues/626#issuecomment-426049082