[原]PHP GD库静态图片格式转换

近期自己一直做的一个图片分享类别的网站,对于上传之后的图片处理转换的一些东西记录分享一下。

PS:目前有很多三方云服务提供图片云,这就不需要我们自己来处理了,他们的方案更好而且还有各种CDN,个人预算能省则省,自己动手丰衣足食,如果是公司项目访问量过大,还是用云服务吧,自己转换挺费资源的。

1.分析

    静态图片的格式一般的我们允许 png,bmp,jpeg虽然gif也可以是静态的但是我把它统一放到动态里面了,因为一旦gif是动态的转换之后就不会再动了,会只保留第一帧,我们暂不讨论他的处理。我总是觉得服务器上放那么多格式的图片很蛋疼,我们还要自己区分,所以我就想用户上传图片上来之后 统一全部都转换成jpeg格式的图片。

2.实现

    一般的我们上传图片都是要做对应的处理的,不管是要压缩缩略图还是添加水印等等操作,我们都需要将图片文件转为内存资源处理。

    GD库已经给我们提供了函数 imagecreatefrompng,imagecreatefromjpeg,imagecreatefromgif,imagecreatefromwbmp给函数一个绝对路径就可以将读入图片资源,

    但是有两点需要注意,imagecreatefromgif 这个不再我们讨论之内,暂时不说,imagecreatefromwbmp 这个看上去好像是bmp但是实际上,这个并非我们常见的.bmp格式的图片处理函数,百度百科上有只言片语可以去看看。很明显这个函数处理不了我们所需求的bmp格式图片。还好各方面人才众多,有人已经实现了相应的函数,我们来借用一下:

     /**
	 * BMP 创建函数
	 * @author simon
	 * @param string $filename path of bmp file
	 * @example who use,who knows
	 * @return resource of GD
	 */
	 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 ){
			$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'] == 32 ){
					$COLOR = unpack( "V", substr( $IMG, $P, 3 ) );
					$B = ord(substr($IMG, $P,1));
					$G = ord(substr($IMG, $P+1,1));
					$R = ord(substr($IMG, $P+2,1));
					$color = imagecolorexact( $res, $R, $G, $B );
					if ( $color == -1 )
						$color = imagecolorallocate( $res, $R, $G, $B );
					$COLOR[0] = $R*256*256+$G*256+$B;
					$COLOR[1] = $color;
				}elseif ( $BMP['bits_per_pixel'] == 24 )
				$COLOR = unpack( "V", substr( $IMG, $P, 3 ) . $VIDE );
				elseif ( $BMP['bits_per_pixel'] == 16 ){
					$COLOR = unpack( "n", substr( $IMG, $P, 2 ) );
					$COLOR[1] = $PALETTE[$COLOR[1] + 1];
				}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;
	}

     有了这个 我们就可以满足处理这三种个是的图片了,首先我们取到上传图片的MIME类型,尽量不要用后缀来区别了,出错率太高了,也不安全。

         switch ($uploaded->mimeType)
		{
			case "image/png":
				$function = 'imagecreatefrompng';break;
			case "image/bmp":
			case "image/x-ms-bmp":
				$function = 'imagecreatefrombmp';break;
			case "image/jpg":
			case "image/jpeg":
				$function = 'imagecreatefromjpeg';break;
			default:
				return array(false, $imageErrorMsg);
		}

     最后使用对应的方法读取一下图片 $imgpow = $function(‘图片路径’); 这样我们就可以的到对应的图片资源了。我们可以对 $imgpow 做任何的处理,只要最后输出的时候我们做一些处理就可以转为相同的格式了。

3.输出

最后处理完成之后我们输出图片并且保存图片。

        //Baseline 转 Progressive
		imageinterlace($imgpow, 1);
		imagejpeg($imgpow,‘图片保存的路径/图片名.jpeg’);
		imagedestroy($imgpow);
		$uploaded->delete();

 

输出对应格式的图片 使用GD库提供的方法即可 imagejpegimagegifimagepng,不论图片是用什么格式读入的,只要是一个图片资源,就可以用不同的格式输出,这里我选用了imagejpeg方法,将所有的图片都输出成了jpeg格式。

PS: imageinterlace($imgpow, 1); 这一句与这次的图片转换并没有太大的关系。他的作用是更改图片在浏览器中的渲染方式。baseline 是从上倒下渲染,而 Progressive 是从低像素到高像素渲染。在网速慢的情况下,Progressive的体验要好一些。

当然,这么好的东西IE一般无福消受,firefox和chrome是支持的。IE还是会用baseline 的方式渲染。