| // | Allan Hansen | // +----------------------------------------------------------------------+ // | module.graphic.bmp.php | // | Module for analyzing BMP graphic files. | // | dependencies: NONE | // +----------------------------------------------------------------------+ // // $Id: module.graphic.bmp.php,v 1.2 2017/08/07 23:48:27 gustafn Exp $ class getid3_bmp extends getid3_handler { public function Analyze() { $getid3 = $this->getid3; // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp // all versions // WORD bfType; // DWORD bfSize; // WORD bfReserved1; // WORD bfReserved2; // DWORD bfOffBits; // shortcuts $getid3->info['bmp']['header']['raw'] = array (); $info_bmp = &$getid3->info['bmp']; $info_bmp_header = &$info_bmp['header']; $info_bmp_header_raw = &$info_bmp_header['raw']; fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); $bmp_header = fread($getid3->fp, 14 + 40); // Magic bytes $info_bmp_header_raw['identifier'] = 'BM'; getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 2, array ( 'filesize' => 4, 'reserved1' => 2, 'reserved2' => 2, 'data_offset' => 4, 'header_size' => 4 ) ); // Check if the hardcoded-to-1 "planes" is at offset 22 or 26 $planes22 = getid3_lib::LittleEndian2Int(substr($bmp_header, 22, 2)); $planes26 = getid3_lib::LittleEndian2Int(substr($bmp_header, 26, 2)); if (($planes22 == 1) && ($planes26 != 1)) { $info_bmp['type_os'] = 'OS/2'; $info_bmp['type_version'] = 1; } elseif (($planes26 == 1) && ($planes22 != 1)) { $info_bmp['type_os'] = 'Windows'; $info_bmp['type_version'] = 1; } elseif ($info_bmp_header_raw['header_size'] == 12) { $info_bmp['type_os'] = 'OS/2'; $info_bmp['type_version'] = 1; } elseif ($info_bmp_header_raw['header_size'] == 40) { $info_bmp['type_os'] = 'Windows'; $info_bmp['type_version'] = 1; } elseif ($info_bmp_header_raw['header_size'] == 84) { $info_bmp['type_os'] = 'Windows'; $info_bmp['type_version'] = 4; } elseif ($info_bmp_header_raw['header_size'] == 100) { $info_bmp['type_os'] = 'Windows'; $info_bmp['type_version'] = 5; } else { throw new getid3_exception('Unknown BMP subtype (or not a BMP file)'); } $getid3->info['fileformat'] = 'bmp'; $getid3->info['video']['dataformat'] = 'bmp'; $getid3->info['video']['lossless'] = true; $getid3->info['video']['pixel_aspect_ratio'] = (float)1; if ($info_bmp['type_os'] == 'OS/2') { // OS/2-format BMP // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm // DWORD Size; /* Size of this structure in bytes */ // DWORD Width; /* Bitmap width in pixels */ // DWORD Height; /* Bitmap height in pixel */ // WORD NumPlanes; /* Number of bit planes (color depth) */ // WORD BitsPerPixel; /* Number of bits per pixel per plane */ getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 18, array ( 'width' => 2, 'height' => 2, 'planes' => 2, 'bits_per_pixel' => 2 ) ); $getid3->info['video']['resolution_x'] = $info_bmp_header_raw['width']; $getid3->info['video']['resolution_y'] = $info_bmp_header_raw['height']; $getid3->info['video']['codec'] = 'BI_RGB '.$info_bmp_header_raw['bits_per_pixel'].'-bit'; $getid3->info['video']['bits_per_sample'] = $info_bmp_header_raw['bits_per_pixel']; if ($info_bmp['type_version'] >= 2) { // DWORD Compression; /* Bitmap compression scheme */ // DWORD ImageDataSize; /* Size of bitmap data in bytes */ // DWORD XResolution; /* X resolution of display device */ // DWORD YResolution; /* Y resolution of display device */ // DWORD ColorsUsed; /* Number of color table indices used */ // DWORD ColorsImportant; /* Number of important color indices */ // WORD Units; /* Type of units used to measure resolution */ // WORD Reserved; /* Pad structure to 4-byte boundary */ // WORD Recording; /* Recording algorithm */ // WORD Rendering; /* Halftoning algorithm used */ // DWORD Size1; /* Reserved for halftoning algorithm use */ // DWORD Size2; /* Reserved for halftoning algorithm use */ // DWORD ColorEncoding; /* Color model used in bitmap */ // DWORD Identifier; /* Reserved for application use */ getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 26, array ( 'compression' => 4, 'bmp_data_size' => 4, 'resolution_h' => 4, 'resolution_v' => 4, 'colors_used' => 4, 'colors_important' => 4, 'resolution_units' => 2, 'reserved1' => 2, 'recording' => 2, 'rendering' => 2, 'size1' => 4, 'size2' => 4, 'color_encoding' => 4, 'identifier' => 4 ) ); $info_bmp_header['compression'] = getid3_bmp::BMPcompressionOS2Lookup($info_bmp_header_raw['compression']); $getid3->info['video']['codec'] = $info_bmp_header['compression'].' '.$info_bmp_header_raw['bits_per_pixel'].'-bit'; } return true; } if ($info_bmp['type_os'] == 'Windows') { // Windows-format BMP // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp // all versions // DWORD biSize; // LONG biWidth; // LONG biHeight; // WORD biPlanes; // WORD biBitCount; // DWORD biCompression; // DWORD biSizeImage; // LONG biXPelsPerMeter; // LONG biYPelsPerMeter; // DWORD biClrUsed; // DWORD biClrImportant; getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 18, array ( 'width' => -4, //signed 'height' => -4, //signed 'planes' => 2, 'bits_per_pixel' => 2, 'compression' => 4, 'bmp_data_size' => 4, 'resolution_h' => -4, //signed 'resolution_v' => -4, //signed 'colors_used' => 4, 'colors_important' => 4 ) ); foreach (array ('width', 'height', 'resolution_h', 'resolution_v') as $key) { $info_bmp_header_raw[$key] = getid3_lib::LittleEndian2Int($info_bmp_header_raw[$key], true); } $info_bmp_header['compression'] = getid3_bmp::BMPcompressionWindowsLookup($info_bmp_header_raw['compression']); $getid3->info['video']['resolution_x'] = $info_bmp_header_raw['width']; $getid3->info['video']['resolution_y'] = $info_bmp_header_raw['height']; $getid3->info['video']['codec'] = $info_bmp_header['compression'].' '.$info_bmp_header_raw['bits_per_pixel'].'-bit'; $getid3->info['video']['bits_per_sample'] = $info_bmp_header_raw['bits_per_pixel']; // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen if (($info_bmp['type_version'] >= 4) || ($info_bmp_header_raw['compression'] == 3)) { $bmp_header .= fread($getid3->fp, 44); // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp // Win95+, WinNT4.0+ // DWORD bV4RedMask; // DWORD bV4GreenMask; // DWORD bV4BlueMask; // DWORD bV4AlphaMask; // DWORD bV4CSType; // CIEXYZTRIPLE bV4Endpoints; // DWORD bV4GammaRed; // DWORD bV4GammaGreen; // DWORD bV4GammaBlue; getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 54, array ( 'red_mask' => 4, 'green_mask' => 4, 'blue_mask' => 4, 'alpha_mask' => 4, 'cs_type' => 4, 'ciexyz_red' => -4, //string 'ciexyz_green' => -4, //string 'ciexyz_blue' => -4, //string 'gamma_red' => 4, 'gamma_green' => 4, 'gamma_blue' => 4 ) ); $info_bmp_header['ciexyz_red'] = getid3_bmp::FixedPoint2_30(strrev($info_bmp_header_raw['ciexyz_red'])); $info_bmp_header['ciexyz_green'] = getid3_bmp::FixedPoint2_30(strrev($info_bmp_header_raw['ciexyz_green'])); $info_bmp_header['ciexyz_blue'] = getid3_bmp::FixedPoint2_30(strrev($info_bmp_header_raw['ciexyz_blue'])); if ($info_bmp['type_version'] >= 5) { $bmp_header .= fread($getid3->fp, 16); // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp // Win98+, Win2000+ // DWORD bV5Intent; // DWORD bV5ProfileData; // DWORD bV5ProfileSize; // DWORD bV5Reserved; getid3_lib::ReadSequence('LittleEndian2Int', $info_bmp_header_raw, $bmp_header, 98, array ( 'intent' => 4, 'profile_data_offset' => 4, 'profile_data_size' => 4, 'reserved3' => 4 ) ); } } return true; } throw new getid3_exception('Unknown BMP format in header.'); } public static function BMPcompressionWindowsLookup($compression_id) { static $lookup = array ( 0 => 'BI_RGB', 1 => 'BI_RLE8', 2 => 'BI_RLE4', 3 => 'BI_BITFIELDS', 4 => 'BI_JPEG', 5 => 'BI_PNG' ); return (isset($lookup[$compression_id]) ? $lookup[$compression_id] : 'invalid'); } public static function BMPcompressionOS2Lookup($compression_id) { static $lookup = array ( 0 => 'BI_RGB', 1 => 'BI_RLE8', 2 => 'BI_RLE4', 3 => 'Huffman 1D', 4 => 'BI_RLE24', ); return (isset($lookup[$compression_id]) ? $lookup[$compression_id] : 'invalid'); } public static function FixedPoint2_30($raw_data) { $binary_string = getid3_lib::BigEndian2Bin($raw_data); return bindec(substr($binary_string, 0, 2)) + (float)(bindec(substr($binary_string, 2, 30)) / 1073741824); // pow(2, 30) = 1073741824 } } ?>