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