BMP file structure analysis by PHP – Part 2


Part 2: The 16-bit per pixel (16bpp) BMP

The bitmap has a maximum of 2^16 colors. if the compression field of the bitmap file is set to bi_rgb, the palette (color table) field does not contain any entries. each word in the bitmap array represents a single pixel. the relative intensities of red, green, and blue are represented with 5 bits for each color component. the value for blue is in the least significant 5 bits, followed by 5 bits each for green and red, respectively. the most significant bit is not used.

if the compression field of the bitmap file is set to bi_bitfields, the palette (color table) field contains three dword color masks that specify the red, green, and blue components, respectively, of each pixel. each word in the bitmap array represents a single pixel.

windows nt specific: when the compression field is set to bi_bitfields, bits set in each dword mask must be contiguous and should not overlap the bits of another mask. all the bits in the pixel do not have to be used.

windows 95 specific: when the compression field is set to bi_bitfields, windows 95 supports only the following 16bpp color masks: a 5-5-5 16-bit image, where the blue mask is 0x001f, the green mask is 0x03e0, and the red mask is 0x7c00; and a 5-6-5 16-bit image, where the blue mask is 0x001f, the green mask is 0x07e0, and the red mask is 0xf800.

We can see in PHOTOSHOP, with BMP 16-bit have 3 modes

  1. 5-5-5 16-bit
  2. 5-6-5 16-bit
  3. 4-4-4 16-bit

Sample pitures:

5-5-5 16-bit

5-6-5 16-bit

4-4-4 16-bit

We consider the example 1 of this tutorial:

The compression field of the bitmap file is set to bi_rgb (0), it’s 5-5-5 16-bit BMP image.

The blue mask is 0x001f (0000000000011111)

The green mask is 0x03e0 (0000001111100000)

The red mask is 0x7c00 (0111110000000000)

<?php
/**
 * @author tutorialspots.com
 * @copyright 2013
 */

$file = 'bmp-16-555.bmp';

$bmp = bmp_header($file, $fh);

// set bytes and padding
if ($bmp['size_bitmap'] == 0)
    $bmp['size_bitmap'] = $bmp['file_size'] - $bmp['bitmap_offset'];
$bmp['bytes_per_pixel'] = $bmp['bits_per_pixel'] / 8;
$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;

fseek($fh, $bmp['bitmap_offset']);
$im = fread($fh, $bmp['size_bitmap']);

// create gd image
$res = imagecreatetruecolor($bmp['width'], $bmp['height']);

$P = 0;
$Y = $bmp['height'] - 1;
 
// loop through the image data beginning with the lower left corner
while ($Y >= 0)
{
    $X = 0;
    while ($X < $bmp['width'])
    {
        $color = unpack("v", substr($im, $P, 2));
 
        $blue = ($color[1] & 0x001f) << 3;// 3 = 8-5 (scale 0-255)
        $green = (($color[1] & 0x03e0) >> 5) << 3 ;//= ($color[1] & 0x03e0) >> 2
        $red = (($color[1] & 0x7c00) >> 10) << 3;//= ($color[1] & 0x7c00) >> 7
         
        $color[1] = ($red << 16) + ($green << 8) + $blue;
        //$color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3);
        //          = (($color[1] & 0x7c00) << 9) + (($color[1] & 0x03e0) << 6) + (($color[1] & 0x001f) << 3);
        imagesetpixel($res, $X, $Y, $color[1]);
        $X++;
        $P += $bmp['bytes_per_pixel'];
    }
    $Y--;
    $P += $bmp['decal'];
}

fclose($fh);

header('Content-type:image/png');
imagepng($res);
?>

We receive the result: http://demo.tutorialspots.com/bmp/ex1.php

With the example 2 of this tutorial:
The compression field is set to bi_bitfields (3).
Read the mask:

$file = 'bmp-16-565.bmp';

$bmp = bmp_header($file, $fh);

// read additional 16bit header, the mask
fseek($fh, 54);
if($bmp['bitmap_offset'] == 66)
    $masks = unpack('VrMask/VgMask/VbMask', fread($fh, 12));
else
    $masks = unpack('VrMask/VgMask/VbMask/VaMask' , fread($fh, 16));   

if(isset($masks['aMask']) && $masks['aMask']==0)
    unset($masks['aMask']);

var_dump($masks)

Result:

array(3) {
  ["rMask"]=>
  int(63488)
  ["gMask"]=>
  int(2016)
  ["bMask"]=>
  int(31)
}

We see that:
Red mask : 63488 = 1111100000000000 = 0xf800
Green mask: 2016 = 0000011111100000 = 0x07e0
Blue mask : 31 = 0000000000011111 = 0x001f

The image is 5-6-5 16-bit BMP image.

<?php
/**
 * @author tutorialspots.com
 * @copyright 2013
 */

$file = 'bmp-16-565.bmp';

$bmp = bmp_header($file, $fh);

// read additional 16bit header, the mask
fseek($fh, 54);
if($bmp['bitmap_offset'] == 66)
    $masks = unpack('VrMask/VgMask/VbMask', fread($fh, 12));
else
    $masks = unpack('VrMask/VgMask/VbMask/VaMask' , fread($fh, 16));   

if(isset($masks['aMask']) && $masks['aMask']==0)
    unset($masks['aMask']);

$rlength = strlen(trim(decbin($masks['rMask']),"0"));
$glength = strlen(trim(decbin($masks['gMask']),"0"));
$blength = strlen(trim(decbin($masks['bMask']),"0"));
 
// set bytes and padding
if ($bmp['size_bitmap'] == 0)
    $bmp['size_bitmap'] = $bmp['file_size'] - $bmp['bitmap_offset'];
$bmp['bytes_per_pixel'] = $bmp['bits_per_pixel'] / 8;
$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;
 
fseek($fh, $bmp['bitmap_offset']);
$im = fread($fh, $bmp['size_bitmap']);

// create gd image
$res = imagecreatetruecolor($bmp['width'], $bmp['height']);

$P = 0;
$Y = $bmp['height'] - 1;
 
// loop through the image data beginning with the lower left corner
while ($Y >= 0)
{
    $X = 0;
    while ($X < $bmp['width'])
    {
        $color = unpack("v", substr($im, $P, 2));
 
        $blue = ($color[1] & $masks['bMask']) << (8-$blength);   
        $green = (($color[1] & $masks['gMask']) >> $blength) << (8-$glength) ; 
        $red = (($color[1] & $masks['rMask']) >> ($blength+$glength)) << (8-$rlength); 
         
        $color[1] = ($red << 16) + ($green << 8) + $blue;
         
        imagesetpixel($res, $X, $Y, $color[1]);
        $X++;
        $P += $bmp['bytes_per_pixel'];
    }
    $Y--;
    $P += $bmp['decal'];
}

fclose($fh);


header('Content-type:image/png');
imagepng($res);

?>

We receive the result: http://demo.tutorialspots.com/bmp/ex2.php

With the example 3 of this tutorial:
The compression field is set to bi_bitfields (3).
Read the mask:

$file = 'bmp-16-444.bmp';

$bmp = bmp_header($file, $fh);

// read additional 16bit header, the mask
fseek($fh, 54);
if($bmp['bitmap_offset'] == 66)
    $masks = unpack('VrMask/VgMask/VbMask', fread($fh, 12));
else
    $masks = unpack('VrMask/VgMask/VbMask/VaMask' , fread($fh, 16));   

if(isset($masks['aMask']) && $masks['aMask']==0)
    unset($masks['aMask']);

var_dump($masks)

Result:

array(3) {
  ["rMask"]=>
  int(3840)
  ["gMask"]=>
  int(240)
  ["bMask"]=>
  int(15)
}

We see that:
Red mask : 3840 = 0000111100000000 = 0x0f00
Green mask: 240 = 0000000011110000 = 0x00f0
Blue mask : 15 = 0000000000001111 = 0x000f

The image is 4-4-4 16-bit BMP image.

We use the same code of ex2.php

<?php
/**
 * @author tutorialspots.com
 * @copyright 2013
 */

$file = 'bmp-16-444.bmp';

$bmp = bmp_header($file, $fh);

// read additional 16bit header, the mask
fseek($fh, 54);
if($bmp['bitmap_offset'] == 66)
    $masks = unpack('VrMask/VgMask/VbMask', fread($fh, 12));
else
    $masks = unpack('VrMask/VgMask/VbMask/VaMask' , fread($fh, 16));   

if(isset($masks['aMask']) && $masks['aMask']==0)
    unset($masks['aMask']);

$rlength = strlen(trim(decbin($masks['rMask']),"0"));
$glength = strlen(trim(decbin($masks['gMask']),"0"));
$blength = strlen(trim(decbin($masks['bMask']),"0"));
 
// set bytes and padding
if ($bmp['size_bitmap'] == 0)
    $bmp['size_bitmap'] = $bmp['file_size'] - $bmp['bitmap_offset'];
$bmp['bytes_per_pixel'] = $bmp['bits_per_pixel'] / 8;
$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;
 
fseek($fh, $bmp['bitmap_offset']);
$im = fread($fh, $bmp['size_bitmap']);

// create gd image
$res = imagecreatetruecolor($bmp['width'], $bmp['height']);

$P = 0;
$Y = $bmp['height'] - 1;
 
// loop through the image data beginning with the lower left corner
while ($Y >= 0)
{
    $X = 0;
    while ($X < $bmp['width'])
    {
        $color = unpack("v", substr($im, $P, 2));
 
        $blue = ($color[1] & $masks['bMask']) << (8-$blength);   
        $green = (($color[1] & $masks['gMask']) >> $blength) << (8-$glength) ; 
        $red = (($color[1] & $masks['rMask']) >> ($blength+$glength)) << (8-$rlength); 
         
        $color[1] = ($red << 16) + ($green << 8) + $blue;
         
        imagesetpixel($res, $X, $Y, $color[1]);
        $X++;
        $P += $bmp['bytes_per_pixel'];
    }
    $Y--;
    $P += $bmp['decal'];
}

fclose($fh);


header('Content-type:image/png');
imagepng($res);

?>

We receive the result: http://demo.tutorialspots.com/bmp/ex3.php

Leave a Reply