Daniel P
Daniel P

Reputation: 4277

Convert Bitmap Files into JPEG using the GD library in PHP

I have been trying to figure out a way to convert bitmap files into a JPEG using the GD library in PHP.

I have tried numerous implementations but nothing seems to work. I have tried to tell my client that they should not use Bitmap files but he insists and quite frankly does not comprehend enough about computers to convert them to JPG on his own.

I can not use ImageMagick on this server and I need a pure GD solution. Thank you in advance for any and all help.

EDIT:

The bitmap images that are being used are 16-bit and that is where the problem is occurring.

I have this function that I have working .... kinda:

function ImageCreateFromBMP($filename) {
    if (! $f1 = fopen($filename,"rb")) return FALSE;

    $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14));
    if ($FILE['file_type'] != 19778) return FALSE;

    $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
        '/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
        '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40));
    $BMP['colors'] = pow(2,$BMP['bits_per_pixel']);

    if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
    $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8;
    $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
    $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4);
    $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4);
    $BMP['decal'] = 4-(4*$BMP['decal']);
    if ($BMP['decal'] == 4) $BMP['decal'] = 0;

    $PALETTE = array();
    if ($BMP['colors'] < 16777216 && $BMP['colors'] != 65536) {
        $PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4));
    }

    $IMG = fread($f1,$BMP['size_bitmap']);
    $VIDE = chr(0);

    $res = imagecreatetruecolor($BMP['width'],$BMP['height']);
    $P = 0;
    $Y = $BMP['height']-1;
    while ($Y >= 0) {
        $X=0;
        while ($X < $BMP['width']) {
            if ($BMP['bits_per_pixel'] == 24)
                $COLOR = unpack("V",substr($IMG,$P,3).$VIDE);
            elseif ($BMP['bits_per_pixel'] == 16) {
                $COLOR = unpack("v",substr($IMG,$P,2));
                $blue  = ($COLOR[1] & 0x001f) << 3;
                $green = ($COLOR[1] & 0x07e0) >> 3;
                $red   = ($COLOR[1] & 0xf800) >> 8;
                $COLOR[1] = $red * 65536 + $green * 256 + $blue;
            }
            elseif ($BMP['bits_per_pixel'] == 8) {
                $COLOR = unpack("n",$VIDE.substr($IMG,$P,1));
                $COLOR[1] = $PALETTE[$COLOR[1]+1];
            }
            elseif ($BMP['bits_per_pixel'] == 4) {
                $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
                if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F);
                $COLOR[1] = $PALETTE[$COLOR[1]+1];
            }
            elseif ($BMP['bits_per_pixel'] == 1) {
                $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
                if     (($P*8)%8 == 0) $COLOR[1] =  $COLOR[1]        >>7;
                elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6;
                elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5;
                elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4;
                elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3;
                elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2;
                elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1;
                elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1);
                $COLOR[1] = $PALETTE[$COLOR[1]+1];
            }
            else
                return FALSE;

            imagesetpixel($res,$X,$Y,$COLOR[1]);

            $X++;
            $P += $BMP['bytes_per_pixel'];
        }
        $Y--;
        $P+=$BMP['decal'];
    }

    fclose($f1);
    return $res;
}

The resulting image is this:

Uploaded Image

If you look at the image on the left hand side you can see that the resulting image is not correctly lined up. The little sliver belongs on the right hand side. Where is the code going wrong? The problem is occurring in the 16-bit else-if.

Thank you again for all the help.

Upvotes: 6

Views: 18315

Answers (4)

mgutt
mgutt

Reputation: 6177

Use this function:

http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm

It supports several bitrates like 16- and 32-bit. Plus it contains some bugfixes regarding missing filesize, negative color palettes, error output, additional 16-bit mask header (this was the main problem on 16-bit) and reduced color palette (biClrUsed).

Hope you like it ;)

Update in 2015: This function is now part of DOMPDF (Search on page for "imagecreatefrombmp") and was brought to perfection. Now it covers compressed 4- and 8-bit, ignores unimportant headers and supports the special 16-bit 565 mask as well.

Upvotes: 11

Kris
Kris

Reputation: 41877

Off the top of my head:

function convert_to_jpeg( $input_path, $output_path )
{
    $image = imagecreatefromstring(file_get_contents($input_path));
    imagejpeg($image, $output_path);
    imagedestroy($image);
}

That'll take any format GD can handle as input, and output a jpeg file. I don't know what version of GD you folks are using, but mine handles .bmp perfectly and so did the version we used at the previous company I worked for. (on Mac OS X 10.6 and CentOS 5 respectively)

edit: forgot imagedestroy! ouch!

Upvotes: 0

schnaader
schnaader

Reputation: 49731

How about the imagejpeg function?

bool imagejpeg ( resource $image [, string $filename [, int $quality ]] )

imagejpeg() creates a JPEG file from the given image .

For help to support BMP format in GD, have a look here, for example.

EDIT: This doesn't support 16 bit images which is correct as the original bitmap specification doesn't support it. In your case, please find out which bit pattern is used to code the color value. I assume it's 5 bits for R and B, 6 bits for G and the order is BGR in this solution (please insert into the code I linked to above):

else if ($bits == 16) {
$gd_scan_line = "";
$j = 0;
while($j < $scan_line_size) {
$byte1 = $scan_line{$j++};
$byte2 = $scan_line{$j++};
$b = chr($byte1 >> 3) * (255 / 31); 
$g = (chr($byte1 & 0x07) + chr($byte2 >> 5)) * (255 / 63);
$r = chr($byte2 & 0x1F) * (255 / 31);
$gd_scan_line .= "\x00$r$g$b";
}

Note that I didn't test this code (specifically, I'm not sure about that scaling to 0..255) and it will only work if 5-6-5 bit pattern has been used (well, it will work with others, too, but the colors will be wrong).

Upvotes: 0

timdev
timdev

Reputation: 62924

While GD does not support BMP natively, a little big of googling provides a few userland implementations of a imagecreatefrombmp() function.

I haven't tried them, but I'm confident at least one of them will work for you.

Upvotes: 0

Related Questions