/*格式组成典型的BMP图像文件由四部分组成: 1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息; 2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息; 3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板; 4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。 */ function imagebmp(&$im, $filename = '', $bit = 8, $compression = 0) { if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) { $bit = 8; //记录每个像素所占计算机字节的位数,默认为8位 } else if ($bit == 32) // todo:32 bit { $bit = 24; } $bits = pow(2, $bit); //表示待创建的图像一共由$bits种颜色组成 // 将图像调整为调色板图像,便于以后绘图,如果是24位BMP位图,那么就不需要这一步 /*调色板是被保存在一个RGBQUAD结构的数组中,该结构指出了每一种颜色的红、绿、蓝的分量值。 *位数组中的每一个索引都对应于一个调色板项(即一个RGBQUAD结构),应用程序将根据这种对 *应关系,将像素索引值转换为像素RGB值(真实的像素颜色) * */ imagetruecolortopalette($im, true, $bits); $width = imagesx($im); $height = imagesy($im); $colors_num = imagecolorstotal($im); //返回的一般都是256 // 颜色索引 $rgb_quad = ''; if ($bit <= 8)//仅针对1,4,8位BMP位图建立颜色索引 { for ($i = 0; $i < $colors_num; $i ++) { $colors = imagecolorsforindex($im, $i);//每一幅图的索引表都是不一样的! $rgb_quad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0"; } // 位图数据 $bmp_data = ''; // 非压缩,即对RGB值不进行压缩,读取时顺序为BGR,高位在前,低位在后 if ($compression == 0 || $bit < 8) { $compression = 0; // 每行字节数必须为4的倍数,补齐。 $extra = ''; $padding = 32 - ( $width * $bit ) % 32 ; if ($padding % 32 != 0) { $extra = str_repeat("\0", $padding); } for ($j = $height - 1; $j >= 0; $j --) { $i = 0; while ($i < $width) { $bin = 0; $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0; for ($k = 8 - $bit; $k >= $limit; $k -= $bit) { $index = imagecolorat($im, $i, $j); $bin |= $index << $k; $i ++; } $bmp_data .= chr($bin); } $bmp_data .= $extra; } } // RLE8 压缩 else if ($compression == 1 && $bit == 8) { for ($j = $height - 1; $j >= 0; $j --) { $last_index = "\0"; $same_num = 0; for ($i = 0; $i <= $width; $i ++) { $index = imagecolorat($im, $i, $j); if ($index !== $last_index || $same_num > 255) { if ($same_num != 0) { $bmp_data .= chr($same_num) . chr($last_index); } $last_index = $index; $same_num = 1; } else { $same_num ++; } } $bmp_data .= "\0\0"; } $bmp_data .= "\0\1"; } $size_quad = strlen($rgb_quad); $size_data = strlen($bmp_data); } else { // 每行字节数必须为4的倍数,补齐。 $extra = ''; $padding = 4 - ($width * ($bit / 8)) % 4; if ($padding % 4 != 0) { $extra = str_repeat("\0", $padding); } // 位图数据 $bmp_data = ''; for ($j = $height - 1; $j >= 0; $j --) { for ($i = 0; $i < $width; $i ++) { $index = imagecolorat($im, $i, $j); $colors = imagecolorsforindex($im, $index); if ($bit == 16) { $bin = 0 << $bit; $bin |= ($colors['red'] >> 3) << 10; $bin |= ($colors['green'] >> 3) << 5; $bin |= $colors['blue'] >> 3; $bmp_data .= pack("v", $bin); } else { $bmp_data .= pack("c*", $colors['blue'], $colors['green'], $colors['red']); } // todo: 32bit; } $bmp_data .= $extra; } $size_quad = 0; $size_data = strlen($bmp_data); $colors_num = 0; } // 位图文件头 $file_header = "BM" . pack("V3", 54 + $size_quad + $size_data, 0, 54 + $size_quad); // 位图信息头 $info_header = pack("V3v2V*", 0x28, $width, $height, 1, $bit, $compression, $size_data, 0, 0, $colors_num, 0); // 写入文件 if ($filename != '') { $fp = fopen($filename, "wb"); fwrite($fp, $file_header); fwrite($fp, $info_header); if($rgb_quad!=='') fwrite($fp, $rgb_quad); fwrite($fp, $bmp_data); fclose($fp); return true; } // 浏览器输出 header("Content-Type: image/bmp"); echo $file_header . $info_header; echo $rgb_quad; echo $bmp_data; return true; }
/** * 類似GD庫打開圖片, 打開bmp格式圖片 * $file : 圖片路徑 * 注意事项:不能读取1位和4位BMP图像,16位BMP图像显示有问题 */ function imagecreatefrombmp($file) { static $CurrentBit; static $echoMode=false;//仅针对4位的图片 $f = fopen($file,"r"); $Header = fread($f,2); if($Header == "BM") { $Size = freaddword($f);// 位图文件的大小,以字节为单位(3-6字节) $Reserved1 = freadword($f);// 位图文件保留字,必须为0(7-8字节) $Reserved2 = freadword($f);// 位图文件保留字,必须为0(9-10字节) $FirstByteOfImage = freaddword($f);// 位图数据的起始位置,以相对于位图 //(11-14字节)文件头的偏移量表示,以字节为单位 $SizeBITMAPINFOHEADER = freaddword($f);// 本结构所占用字节数(15-18字节) $Width = freaddword($f);// 位图的宽度,以像素为单位(19-22字节) $Height = freaddword($f);// 位图的高度,以像素为单位(23-26字节) $biPlanes = freadword($f);// 目标设备的级别,必须为1(27-28字节) $biBitCount = freadword($f);// 每个像素所需的位数,必须是1(双色),(29-30字节) // 4(16色),8(256色)16(高彩色)或24(真彩色)之一 $RLECompression = freaddword($f);// 位图压缩类型,必须是 0(不压缩),(31-34字节) // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 $WidthxHeight = freaddword($f);// 位图的大小,以字节为单位(35-38字节) $biXPelsPerMeter = freaddword($f);// 位图水平分辨率,每米像素数(39-42字节) $biYPelsPerMeter = freaddword($f);// 位图垂直分辨率,每米像素数(43-46字节) $NumberOfPalettesUsed = freaddword($f);// 位图实际使用的颜色表中的颜色数(47-50字节) $NumberOfImportantColors = freaddword($f);// 位图显示过程中重要的颜色数(51-54字节) /* * 位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数: * 当biBitCount=1时,8个像素占1个字节; * 当biBitCount=4时,2个像素占1个字节; * 当biBitCount=8时,1个像素占1个字节; * 当biBitCount=24时,1个像素占3个字节; * Windows规定一个扫描行所占的字节数必须是 * 4的倍数(即以long为单位),不足的以0填充, */ if($biBitCount < 16)//1位和4位图像仍不能读取。 { $img = imagecreate($Width, $Height); //读取颜色索引表,这个只在24位以下BMP图片中才需要 $Colors = pow(2, $biBitCount); for($p=0; $p<$Colors; $p++) {//$p代表索引值 $B = freadbyte($f); $G = freadbyte($f); $R = freadbyte($f); $Reserved = freadbyte($f); $Palette[] = imagecolorallocate($img, $R, $G, $B); } if($RLECompression == 0) { $Zbytek = (4-ceil(($Width/(8/$biBitCount)))%4)%4; for($y=$Height-1; $y>=0; $y--) { $CurrentBit = 0; for($x=0; $x<$Width; $x++) { $C = freadbits($f, $biBitCount,$echoMode); //从索引表里找到相应的RGB颜色代码去填充图像该点的颜色 imagesetpixel($img, $x, $y, $Palette[$C]); } for($g=0; $g<$Zbytek; $g++) { freadbyte($f); } } } } if($RLECompression == 1) //$BI_RLE8 { $y = $Height; $pocetb = 0; while(true) { $y--; $prefix = freadbyte($f); $suffix = freadbyte($f); $pocetb += 2; $echoit = false; if($echoit) { echo "Prefix: $prefix Suffix: $suffix<BR>"; } if(($prefix == 0) && ($suffix == 1)) { break; } if(feof($f)) { break; } while(!(($prefix == 0) && ($suffix == 0))) { if($prefix==0) { $pocet = $suffix; $Data .= fread($f,$pocet); $pocetb += $pocet; if($pocetb%2 == 1) { freadbyte($f); $pocetb++; } } if($prefix > 0) { $pocet = $prefix; for($r=0; $r<$pocet; $r++) { $Data.=chr($suffix); } } $prefix = freadbyte($f); $suffix = freadbyte($f); $pocetb += 2; if($echoit) { echo "Prefix: $prefix Suffix: $suffix<BR>"; } } for($x=0; $x<strlen($Data); $x++) { imagesetpixel($img,$x,$y,$Palette[ord($Data[$x])]); } $Data=""; } } if($RLECompression == 2) { $y = $Height; $pocetb = 0; while(true) { $y--; $prefix = freadbyte($f); $suffix = freadbyte($f); $pocetb += 2; $echoit = false; if($echoit) { echo "Prefix: $prefix Suffix: $suffix<BR>"; } if(($prefix == 0) && ($suffix==1)) { break; } if(feof($f)) { break; } while(!(($prefix == 0) && ($suffix == 0))) { if($prefix == 0) { $pocet = $suffix; $CurrentBit = 0; for($h=0; $h<$pocet; $h++) { $Data.=chr(freadbits($f,4,$echoMode)); } if($CurrentBit != 0) { freadbits($f,4,$echoMode);//不知道这里的代码对不对 } $pocetb += ceil(($pocet/2)); if($pocetb%2 == 1) { freadbyte($f); $pocetb++; } } if($prefix > 0) { $pocet = $prefix; $i = 0; for($r=0; $r<$pocet; $r++) { if($i%2 == 0) { $Data .= chr($suffix%16); } else { $Data .= chr(floor($suffix/16)); } $i++; } } $prefix = freadbyte($f); $suffix = freadbyte($f); $pocetb += 2; if($echoit) { echo "Prefix: $prefix Suffix: $suffix<BR>"; } } for($x=0; $x<strlen($Data); $x++) { imagesetpixel($img, $x, $y, $Palette[ord($Data[$x])]); } $Data=""; } } if($biBitCount >= 16) { $img = imagecreatetruecolor($Width, $Height); $Zbytek = $Width%4;//微软规定每行必须是4的倍数,所以不足4的字节全部用0填充。 for($y=$Height-1; $y>=0; $y--) { for($x=0; $x<$Width; $x++) { if($biBitCount == 16){ $t1 = freadbyte($f); $t2 = freadbyte($f);//freadbyte($f);freadbyte($f); $B = $t1 >> 3 & 0x1f; $G = ((( $t1 & 0x07 ) << 3)&0xff | (( $t2 & 0xe0 ) >> 5)&0xff) & 0xff; $R = $t2 & 0x3e; }else{ $B = freadbyte($f); $G = freadbyte($f); $R = freadbyte($f); } if($biBitCount == 32)freadbyte($f); $color = imagecolorexact($img, $R, $G, $B); if($color==-1) { $color=imagecolorallocate($img, $R, $G, $B); } imagesetpixel($img, $x, $y, $color); } for($z=0; $z<$Zbytek; $z++) { freadbyte($f); } } } return $img; } fclose($f); } function freadbyte($f) { return ord(fread($f, 1)); } function freadbits($f, $biBitCount,&$echoMode='') { $str=''; if($biBitCount==4){ if($echoMode==false){ $echoMode=true; $str = freadbyte($f); fseek($f, -1,SEEK_CUR); $str = $str >> 4; return $str & 0x0f; }else{ $str = freadbyte($f); $echoMode = false; return $str & 0x0f; } } if($biBitCount == 8){ $str=freadbyte($f); return $str & 0xff; } } function freadword($f) { $b1 = freadbyte($f); $b2 = freadbyte($f); return $b2*256 + $b1; } function freaddword($f) { $b1 = freadword($f); $b2 = freadword($f); return $b2*65536 + $b1; }