Skip to content

Document imagebmp() bit depth #4339

Open
@mmellon

Description

@mmellon

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions