php 圖片壓縮類

 PHP圖片壓縮類,部分來源於網絡(侵則刪),部分自己改寫拼湊成一個類。該類封裝了了PHP7沒有的imagecreatefrombmp等的相關方法(7.2有提供這個方法)


<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/12/18
 * Time: 9:56
 */
namespace App\Service\ImageDeal;

/**
 * 圖片壓縮類:通過縮放來壓縮。如果要保持源圖比例,把參數$percent保持爲1即可。
 * 即使原比例壓縮,也可大幅度縮小。數碼相機4M圖片。也可以縮爲700KB左右。如果縮小比例,則體積會更小。
 * 結果:可保存、可直接顯示。
 *
 * 使用方法
 *  $dst_img = './ss.bmp'; //可加存放路徑
    $source = './ss.bmp'; //可加存放路徑(可以直接保留回原圖)
    $percent = 1;  #原圖壓縮,不縮放
    $image = (new ImageCompressService($source,$percent))->compressImg($dst_img);       //調用示例
 */
Class ImageCompressService{

    private $src;
    public $image;
    private $imageinfo;
    private $percent = 0.5;
    /**
     * 圖片壓縮
     * @param $src 源圖
     * @param float $percent  壓縮比例
     */
    public function __construct($src, $percent=1)
    {
        $this->src = $src;
        $this->percent = $percent;
    }


    /** 高清壓縮圖片
     * @param string $saveName  提供圖片名(可不帶擴展名,用源圖擴展名)用於保存。或不提供文件名直接顯示
     */
    public function compressImg($saveName='')
    {
        $this->_openImage();
        if(!empty($saveName)) $this->_saveImage($saveName);  //保存
        else $this->_showImage();
    }

    /**
     * 內部:打開圖片
     */
    private function _openImage()
    {
        list($width, $height, $type, $attr) = getimagesize($this->src);
        $this->imageinfo = array(
            'width'=>$width,
            'height'=>$height,
            'type'=>image_type_to_extension($type,false),
            'attr'=>$attr
        );
        if($this->imageinfo['type'] == 'bmp'){
            $this->image = $this->imagecreatefrombmp($this->src);  //  自己編寫的bmp處理方法
        }else{
            $fun = "imagecreatefrom".$this->imageinfo['type'];  //調用php方法
            $this->image = $fun($this->src);
        }
        $this->_thumpImage();
    }
    /**
     * 內部:操作圖片
     */
    private function _thumpImage()
    {
        $new_width = $this->imageinfo['width'] * $this->percent;
        $new_height = $this->imageinfo['height'] * $this->percent;
        $image_thump = imagecreatetruecolor($new_width,$new_height);
        //將原圖複製帶圖片載體上面,並且按照一定比例壓縮,極大的保持了清晰度
        imagecopyresampled($image_thump,$this->image,0,0,0,0,$new_width,$new_height,$this->imageinfo['width'],$this->imageinfo['height']);
        imagedestroy($this->image);
        $this->image = $image_thump;
    }

    /**
     * 輸出圖片:保存圖片則用saveImage()
     */
    private function _showImage()
    {
        header('Content-Type: image/'.$this->imageinfo['type']);
        $funcs = "image".$this->imageinfo['type'];
        $funcs($this->image);
    }
    /**
     * 保存圖片到硬盤:
     * @param  string $dstImgName  1、可指定字符串不帶後綴的名稱,使用源圖擴展名 。2、直接指定目標圖片名帶擴展名。
     */
    private function _saveImage($dstImgName)
    {
        if(empty($dstImgName)) return false;
        $allowImgs = ['.jpg', '.jpeg', '.png', '.bmp', '.wbmp','.gif'];   //如果目標圖片名有後綴就用目標圖片擴展名 後綴,如果沒有,則用源圖的擴展名
        $dstExt =  strrchr($dstImgName ,".");
        $sourseExt = strrchr($this->src ,".");
        if(!empty($dstExt)) $dstExt =strtolower($dstExt);
        if(!empty($sourseExt)) $sourseExt =strtolower($sourseExt);

        //有指定目標名擴展名
        if(!empty($dstExt) && in_array($dstExt,$allowImgs)){
            $dstName = $dstImgName;
        }elseif(!empty($sourseExt) && in_array($sourseExt,$allowImgs)){
            $dstName = $dstImgName.$sourseExt;
        }else{
            $dstName = $dstImgName.$this->imageinfo['type'];
        }

        if($this->imageinfo['type'] == 'bmp'){
            $this->imagebmp($this->image,$dstName);     //調用外部方法
        }else{
            $funcs = "image".$this->imageinfo['type'];
            $funcs($this->image,$dstName);
        }
    }

    /**
     * 銷燬圖片
     */
    public function __destruct(){
        if(is_resource($this->image)){
            imagedestroy($this->image);
        }
    }


    /**
     * 補全bmp相關的方法(函數)
     */
    function imagecreatefrombmp($file)
    {
        global $CurrentBit, $echoMode;
        $f=fopen($file,"r");
        $Header=fread($f,2);
        if($Header=="BM")
        {
            $Size=$this->freaddword($f);
            $Reserved1=$this->freadword($f);
            $Reserved2=$this->freadword($f);
            $FirstByteOfImage=$this->freaddword($f);
            $SizeBITMAPINFOHEADER=$this->freaddword($f);
            $Width=$this->freaddword($f);
            $Height=$this->freaddword($f);
            $biPlanes=$this->freadword($f);
            $biBitCount=$this->freadword($f);
            $RLECompression=$this->freaddword($f);
            $WidthxHeight=$this->freaddword($f);
            $biXPelsPerMeter=$this->freaddword($f);
            $biYPelsPerMeter=$this->freaddword($f);
            $NumberOfPalettesUsed=$this->freaddword($f);
            $NumberOfImportantColors=$this->freaddword($f);
            if($biBitCount<24)
            {
                $img=imagecreate($Width,$Height);
                $Colors=pow(2,$biBitCount);
                for($p=0;$p<$Colors;$p++)
                {
                    $B=$this->freadbyte($f);
                    $G=$this->freadbyte($f);
                    $R=$this->freadbyte($f);
                    $Reserved=$this->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=$this->freadbits($f,$biBitCount);
                            imagesetpixel($img,$x,$y,$Palette[$C]);
                        }
                        if($CurrentBit!=0) {$this->freadbyte($f);}
                        for($g=0;$g<$Zbytek;$g++)
                            $this->freadbyte($f);
                    }
                }
            }
            if($RLECompression==1) //$BI_RLE8
            {
                $y=$Height;
                $pocetb=0;
                while(true)
                {
                    $y--;
                    $prefix=$this->freadbyte($f);
                    $suffix=$this->freadbyte($f);
                    $pocetb+=2;
                    $echoit=false;
                    if($echoit)echo "Prefix: $prefix Suffix: $suffix<BR>";
                    if(($prefix==0)and($suffix==1)) break;
                    if(feof($f)) break;
                    while(!(($prefix==0)and($suffix==0)))
                    {
                        if($prefix==0)
                        {
                            $pocet=$suffix;
                            $Data.=fread($f,$pocet);
                            $pocetb+=$pocet;
                            if($pocetb%2==1) {$this->freadbyte($f); $pocetb++;}
                        }
                        if($prefix>0)
                        {
                            $pocet=$prefix;
                            for($r=0;$r<$pocet;$r++)
                                $Data.=chr($suffix);
                        }
                        $prefix=$this->freadbyte($f);
                        $suffix=$this->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) //$BI_RLE4
            {
                $y=$Height;
                $pocetb=0;
                /*while(!feof($f))
                echo freadbyte($f)."_".freadbyte($f)."<BR>";*/
                while(true)
                {
//break;
                    $y--;
                    $prefix=$this->freadbyte($f);
                    $suffix=$this->freadbyte($f);
                    $pocetb+=2;
                    $echoit=false;
                    if($echoit)echo "Prefix: $prefix Suffix: $suffix<BR>";
                    if(($prefix==0)and($suffix==1)) break;
                    if(feof($f)) break;
                    while(!(($prefix==0)and($suffix==0)))
                    {
                        if($prefix==0)
                        {
                            $pocet=$suffix;
                            $CurrentBit=0;
                            for($h=0;$h<$pocet;$h++)
                                $Data.=chr($this->freadbits($f,4));
                            if($CurrentBit!=0) $this->freadbits($f,4);
                            $pocetb+=ceil(($pocet/2));
                            if($pocetb%2==1) {$this->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=$this->freadbyte($f);
                        $suffix=$this->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==24)
            {
                $img=imagecreatetruecolor($Width,$Height);
                $Zbytek=$Width%4;
                for($y=$Height-1;$y>=0;$y--)
                {
                    for($x=0;$x<$Width;$x++)
                    {
                        $B=$this->freadbyte($f);
                        $G=$this->freadbyte($f);
                        $R=$this->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++)
                        $this->freadbyte($f);
                }
            }
            return $img;
        }
        fclose($f);
    }


    function freadbits($f, $biBitCount,&$echoMode='')
    {   $str='';
        if($biBitCount==4){
            if($echoMode==false){
                $echoMode=true;
                $str = $this->freadbyte($f);
                fseek($f, -1,SEEK_CUR);
                $str = $str >> 4;
                return $str & 0x0f;
            }else{
                $str = $this->freadbyte($f);
                $echoMode = false;
                return $str & 0x0f;
            }
        }
        if($biBitCount == 8){
            $str=$this->freadbyte($f);
            return $str & 0xff;
        }
    }
    
    
    function freadbyte($f)
    {
        return ord(fread($f,1));
    }
    function freadword($f)
    {
        $b1=$this->freadbyte($f);
        $b2=$this->freadbyte($f);
        return $b2*256+$b1;
    }
    function freaddword($f)
    {
        $b1=$this->freadword($f);
        $b2=$this->freadword($f);
        return $b2*65536+$b1;
    }
    /*格式組成典型的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;
    }

}



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章