You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将C++ BMP写入代码转Java:fwrite行疑问及Java实现问题

Hey there! Let's unpack this confusion about the C++ code first, then walk through how to get your Java implementation right.

Understanding the C++ fwrite Line

That single fwrite line works because of two key facts about C++'s std::vector and how BMP pixel data is stored:

  • std::vector uses contiguous memory: When you take &outPixels[0], you're getting the memory address of the first pixel in the vector. All subsequent pixels are stored immediately after this address in a single, unbroken block of memory.
  • The fwrite parameters are doing the heavy lifting: The function signature is fwrite(const void *ptr, size_t size, size_t count, FILE *stream). Here:
    • ptr is the start of your pixel data (&outPixels[0])
    • size is set to infoHeader.biSizeImage — the total number of bytes needed for all pixel data (including row alignment padding)
    • count is 1, meaning "write 1 block of size bytes"
      So instead of looping through each pixel, fwrite just dumps the entire contiguous block of pixel data in one go. Since each TPixelBGRU8 is a 3-byte std::array, the vector's memory is exactly a sequence of BGR bytes matching what the BMP format expects.
Porting to Java: Common BufferedImage.setRGB Pitfalls

Your suspicion about setRGB being problematic is spot-on. Here's why:

  • Color channel mismatch: setRGB expects an int in ARGB format (alpha, red, green, blue), but your data is BGR (no alpha). Directly converting without reordering channels will result in swapped colors (e.g., red and blue will be inverted).
  • Performance and alignment: Calling setRGB in a loop for every pixel is slow, and it doesn't handle BMP's mandatory 4-byte row alignment automatically — something your C++ code's biSizeImage already accounts for.
  • Non-contiguous storage: Depending on the BufferedImage type you choose, the underlying pixel data might not be stored in a contiguous byte array, which breaks the direct memory dump approach from C++.
Correct Java Implementation Approach

To mirror the C++ behavior efficiently and accurately, use a BufferedImage.TYPE_3BYTE_BGR — this type stores pixels as a contiguous array of BGR bytes (exactly matching your C++ TPixelBGRU8), and lets you directly manipulate the raw byte data. Here's a step-by-step implementation:

  1. Set up the BufferedImage and raw pixel array

    int imageWidth = ...; // Match your C++ image width
    int imageHeight = ...; // Match your C++ image height
    
    // Create a 24-bit BGR BufferedImage (matches TPixelBGRU8)
    BufferedImage bmpImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR);
    byte[] rawPixelData = ((DataBufferByte) bmpImage.getRaster().getDataBuffer()).getData();
    
  2. Calculate row alignment (critical for BMP)
    BMP requires each row of pixels to be padded to a multiple of 4 bytes. Calculate the actual byte count per row:

    int bytesPerPixel = 3;
    int unpaddedRowSize = imageWidth * bytesPerPixel;
    int paddedRowSize = (unpaddedRowSize + 3) / 4 * 4; // Round up to next multiple of 4
    
  3. Fill the raw pixel array
    Assuming you have your pixels stored in an ArrayList<byte[]> where each element is a 3-byte BGR array:

    int dataIndex = 0;
    // BMP stores pixels from bottom to top, so iterate rows in reverse if needed
    for (int y = imageHeight - 1; y >= 0; y--) {
        int pixelStartIndex = y * imageWidth;
        // Write pixels for this row
        for (int x = 0; x < imageWidth; x++) {
            byte[] bgrPixel = yourPixelList.get(pixelStartIndex + x);
            rawPixelData[dataIndex++] = bgrPixel[0]; // Blue
            rawPixelData[dataIndex++] = bgrPixel[1]; // Green
            rawPixelData[dataIndex++] = bgrPixel[2]; // Red
        }
        // Add padding bytes if needed
        while (dataIndex % 4 != 0) {
            rawPixelData[dataIndex++] = 0;
        }
    }
    
  4. Write the complete BMP file
    You can use ImageIO.write() for simplicity, but if you want full control matching your C++ code (e.g., custom headers), write the BMP headers manually then dump the raw pixel data:

    try (FileOutputStream fos = new FileOutputStream("output.bmp")) {
        // Write BMP File Header (14 bytes)
        byte[] fileHeader = new byte[14];
        fileHeader[0] = 'B';
        fileHeader[1] = 'M';
        int totalFileSize = 14 + 40 + paddedRowSize * imageHeight; // 40 = BITMAPINFOHEADER size
        // Pack file size into 4 little-endian bytes
        fileHeader[2] = (byte) (totalFileSize & 0xFF);
        fileHeader[3] = (byte) ((totalFileSize >> 8) & 0xFF);
        fileHeader[4] = (byte) ((totalFileSize >> 16) & 0xFF);
        fileHeader[5] = (byte) ((totalFileSize >> 24) & 0xFF);
        fileHeader[10] = (byte) (14 + 40); // Offset to pixel data
        fos.write(fileHeader);
    
        // Write BITMAPINFOHEADER (40 bytes)
        byte[] infoHeader = new byte[40];
        infoHeader[0] = 40; // Header size
        // Pack width and height (little-endian)
        infoHeader[4] = (byte) (imageWidth & 0xFF);
        infoHeader[5] = (byte) ((imageWidth >> 8) & 0xFF);
        infoHeader[6] = (byte) ((imageWidth >> 16) & 0xFF);
        infoHeader[7] = (byte) ((imageWidth >> 24) & 0xFF);
        infoHeader[8] = (byte) (imageHeight & 0xFF);
        infoHeader[9] = (byte) ((imageHeight >> 8) & 0xFF);
        infoHeader[10] = (byte) ((imageHeight >> 16) & 0xFF);
        infoHeader[11] = (byte) ((imageHeight >> 24) & 0xFF);
        infoHeader[12] = 1; // Number of color planes
        infoHeader[14] = 24; // Bits per pixel
        int pixelDataSize = paddedRowSize * imageHeight;
        infoHeader[20] = (byte) (pixelDataSize & 0xFF);
        infoHeader[21] = (byte) ((pixelDataSize >> 8) & 0xFF);
        infoHeader[22] = (byte) ((pixelDataSize >> 16) & 0xFF);
        infoHeader[23] = (byte) ((pixelDataSize >> 24) & 0xFF);
        fos.write(infoHeader);
    
        // Write raw pixel data
        fos.write(rawPixelData);
    } catch (IOException e) {
        e.printStackTrace();
    }
    
Key Notes
  • Row order: BMP stores pixels starting from the bottom row. If your C++ code builds outPixels from top to bottom, make sure to iterate rows in reverse in Java (like the example above) to avoid a flipped image.
  • Header consistency: Double-check that your Java header values match what your C++ code uses (e.g., if your C++ uses a different info header size, adjust the byte counts accordingly).

内容的提问来源于stack exchange,提问作者George Zorikov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:36:15