Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document imagebmp() bit depth #4339

Open
mmellon opened this issue Dec 17, 2024 · 3 comments
Open

Document imagebmp() bit depth #4339

mmellon opened this issue Dec 17, 2024 · 3 comments

Comments

@mmellon
Copy link

mmellon commented Dec 17, 2024

Description

We have a business case where we need PHP to return a 1-bit depth BMP image to the client. This is because the client is outputting the image to a thermal receipt printer which can only handle 1-bit or 4-bit depth images. The imagebmp() function does not currently have a parameter to specify the color depth; it probably should. The documentation did not specify what the color depth of the generated image was by default.

Ideally, it would be nice to have a function with the following signature:

imagebmp(GdImage $image, resource|string|null $file = null, bool $compressed = true, int $colorDepth = 24): bool

We worked around this issue by writing a user-defined function:

// The PHP GD extension cannot output a 1-bit depth BMP with its built-in functions, so you have to DIY.
// See https://en.wikipedia.org/wiki/BMP_file_format
function output1BitBMP($gdImage) {
    $width = imagesx($gdImage);
    $height = imagesy($gdImage);

    // Create a 1-bit BMP header
    $fileHeaderSize = 14;
    $infoHeaderSize = 40;
    $paletteSize = 8; // 2 colors * 4 bytes per color
    $rowSize = ceil($width / 8);
    $rowSizePadded = ($rowSize + 3) & ~3; // Rows are padded to multiples of 4 bytes
    $imageSize = $rowSizePadded * $height;
    $fileSize = $fileHeaderSize + $infoHeaderSize + $paletteSize + $imageSize;

    // BMP file header
    $fileHeader = pack('vVvvV', 0x4D42, $fileSize, 0, 0, $fileHeaderSize + $infoHeaderSize + $paletteSize);

    // BMP info header
    $infoHeader = pack('V3v2V6', $infoHeaderSize, $width, $height, 1, 1, 0, $imageSize, 0, 0, 2, 0);

    // BMP color palette (black and white)
    $palette = pack('V2', 0x00000000, 0x00FFFFFF);

    // BMP pixel data
    $pixelData = '';
    for ($y = $height - 1; $y >= 0; $y--) {
        $row = '';
        for ($x = 0; $x < $width; $x += 8) {
            $byte = 0;
            for ($bit = 0; $bit < 8; $bit++) {
                if ($x + $bit < $width) {
                    $color = imagecolorat($gdImage, $x + $bit, $y);
                    $gray = (imagecolorsforindex($gdImage, $color)['red'] + imagecolorsforindex($gdImage, $color)['green'] + imagecolorsforindex($gdImage, $color)['blue']) / 3;
                    $byte |= ($gray < 128 ? 1 : 0) << (7 - $bit);
                }
            }
            $row .= chr($byte);
        }
        // Pad the row to a multiple of 4 bytes
        while (strlen($row) % 4 !== 0) {
            $row .= chr(0);
        }
        $pixelData .= $row;
    }

    // Output the BMP
    header('Content-Type: image/bmp');
    echo $fileHeader . $infoHeader . $palette . $pixelData;
}
@mmellon mmellon changed the title imagebmp support for specific bit-depths imagebmp support for specific color depths Dec 17, 2024
@mmellon
Copy link
Author

mmellon commented Dec 17, 2024

On behalf of my employer, as a corporate officer with contract binding authority, I release the code in my preceding comment into the public domain.

@cmb69
Copy link
Member

cmb69 commented Dec 17, 2024

Makes generally sense to me (and thanks for the code :) I'm not sure whether the color depth should even be specified by the user; what happens if there are more colors in the palette (or in truecolor images).

Anyhow, that would need first be addressed upstream, so please file an ticket there.

@cmb69
Copy link
Member

cmb69 commented Dec 23, 2024

Forwarded to libgd/libgd#911.

The imagebmp() function does not currently have a parameter to specify the color depth; it probably should. The documentation did not specify what the color depth of the generated image was by default.

libgd uses a simple solution: truecolor images are stored as 24bit, palette images are stored as 8bit BMPs. I agree that this should be documented.

@cmb69 cmb69 transferred this issue from php/php-src Dec 23, 2024
@cmb69 cmb69 changed the title imagebmp support for specific color depths Document imagebmp() bit depth Dec 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants