首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 縮圖類 PicThumb:支援 ImageMagick/GD 雙引擎、裁剪、水印、背景填充

     編輯:PHP 相關知識

PHP 專業縮圖類 PicThumb 完整實作

PicThumb 是功能完整的縮圖產生類,支援 ImageMagick(效能佳、功能全)與 GD(相容性佳)雙引擎,提供等比縮放、裁剪、背景填充、浮水印等功能。

核心功能

功能ImageMagickGD
等比縮放 (fit)✓✓
區域裁剪 (crop)✓✓
背景填充✓✓
浮水印 (透明度/位置)✓ 完整支援⚠ 僅支援不透明水印
CMYK 轉 RGB✓ 自動處理✗
GIF 動畫處理✓✗

PicThumb 類別核心程式碼

<?php
/** 縮圖生成類,支援 ImageMagick 及 GD 庫
 * Date: 2013-07-15
 * Author: fdipzone
 * Ver: 1.2
 */
class PicThumb {
    private $_log = null;
    private $_handler = null;     // imagemagick / gd
    private $_type = 'fit';       // fit / crop
    private $_source = null;
    private $_dest = null;
    private $_watermark = null;
    private $_opacity = 75;
    private $_gravity = 'SouthEast';
    private $_geometry = '+10+10';
    private $_croppos = 'TL';
    private $_bgcolor = null;
    private $_quality = 90;
    private $_width = null;
    private $_height = null;

    public function __construct($logfile = '') {
        if ($logfile) $this->_log = $logfile;
    }

    /** 設定參數 */
    public function set_config($param = array()) {
        $this->_handler = $this->exists($param, 'handler') ? strtolower($param['handler']) : null;
        $this->_type = $this->exists($param, 'type') ? strtolower($param['type']) : 'fit';
        $this->_watermark = $this->exists($param, 'watermark') ? $param['watermark'] : null;
        $this->_opacity = $this->exists($param, 'opacity') ? $param['opacity'] : 75;
        $this->_gravity = $this->exists($param, 'gravity') ? $param['gravity'] : 'SouthEast';
        $this->_geometry = $this->exists($param, 'geometry') ? $param['geometry'] : '+10+10';
        $this->_croppos = $this->exists($param, 'croppos') ? $param['croppos'] : 'TL';
        $this->_bgcolor = $this->exists($param, 'bgcolor') ? $param['bgcolor'] : null;
        $this->_quality = $this->exists($param, 'quality') ? $param['quality'] : 90;
        $this->_width = $this->exists($param, 'width') ? $param['width'] : null;
        $this->_height = $this->exists($param, 'height') ? $param['height'] : null;
    }

    /** 產生縮圖主入口 */
    public function create_thumb($source, $dest) {
        if (!$this->check_handler()) { $this->to_log('handler not installed'); return false; }
        if (!is_numeric($this->_width) || !is_numeric($this->_height) || $this->_width <= 0 || $this->_height <= 0) {
            $this->to_log('width or height invalid'); return false;
        }
        if (!file_exists($source)) { $this->to_log($source . ' not exists'); return false; }
        if (!$this->create_dirs($dest)) { $this->to_log(dirname($dest) . ' create fail'); return false; }

        $this->_source = $source;
        $this->_dest = $dest;

        switch ($this->_type) {
            case 'fit': return $this->_handler == 'imagemagick' ? $this->fit() : $this->gd_fit();
            case 'crop': return $this->_handler == 'imagemagick' ? $this->crop() : $this->gd_crop();
            default: $this->to_log('unknown type'); return false;
        }
    }

    /** ImageMagick 等比縮放 */
    private function fit() {
        $bgcolor = $this->_bgcolor ? sprintf(" -background '%s' -gravity center -extent '%sx%s' ", $this->_bgcolor, $this->_width, $this->_height) : "";
        $source_info = getimagesize($this->_source);
        $colorspace = (!isset($source_info['channels']) || $source_info['channels'] != 3) ? ' -colorspace RGB ' : '';
        $cmd = sprintf("convert -resize '%sx%s' '%s' %s -quality %s %s '%s'",
            $this->_width, $this->_height, $this->_source, $bgcolor, $this->_quality, $colorspace, $this->_dest);
        $this->to_log($cmd);
        exec($cmd);
        $this->add_watermark($this->_dest);
        return is_file($this->_dest);
    }

    /** ImageMagick 裁剪 */
    private function crop() {
        list($pic_w, $pic_h) = $this->get_size();
        list($offset_w, $offset_h) = $this->get_crop_offset($pic_w, $pic_h);
        $source_info = getimagesize($this->_source);
        $colorspace = (!isset($source_info['channels']) || $source_info['channels'] != 3) ? ' -colorspace RGB ' : '';
        $cmd = sprintf("convert -resize '%sx%s' '%s' -quality %s %s -crop %sx%s+%s+%s +repage '%s'",
            $pic_w, $pic_h, $this->_source, $this->_quality, $colorspace,
            $this->_width, $this->_height, $offset_w, $offset_h, $this->_dest);
        $this->to_log($cmd);
        exec($cmd);
        $this->add_watermark($this->_dest);
        return is_file($this->_dest);
    }

    /** GD 等比縮放 */
    private function gd_fit() {
        list($pic_w, $pic_h) = $this->get_size();
        list($ow, $oh, $otype) = getimagesize($this->_source);
        $source_img = $this->create_source_image($this->_source, $otype);
        $new_img = imagecreatetruecolor($pic_w, $pic_h);
        imagecopyresampled($new_img, $source_img, 0, 0, 0, 0, $pic_w, $pic_h, $ow, $oh);

        if ($this->_bgcolor) {
            $bg_img = imagecreatetruecolor($this->_width, $this->_height);
            $rgb = $this->hex2rgb($this->_bgcolor);
            $bgcolor = imagecolorallocate($bg_img, $rgb['r'], $rgb['g'], $rgb['b']);
            imagefill($bg_img, 0, 0, $bgcolor);
            imagecopy($bg_img, $new_img, (int)(($this->_width - $pic_w)/2), (int)(($this->_height - $pic_h)/2), 0, 0, $pic_w, $pic_h);
            $new_img = $bg_img;
        }
        $this->save_image($new_img, $this->_dest);
        $this->add_watermark($this->_dest);
        return is_file($this->_dest);
    }

    /** GD 裁剪 */
    private function gd_crop() {
        list($pic_w, $pic_h) = $this->get_size();
        list($offset_w, $offset_h) = $this->get_crop_offset($pic_w, $pic_h);
        list($ow, $oh, $otype) = getimagesize($this->_source);
        $source_img = $this->create_source_image($this->_source, $otype);
        $tmp_img = imagecreatetruecolor($pic_w, $pic_h);
        imagecopyresampled($tmp_img, $source_img, 0, 0, 0, 0, $pic_w, $pic_h, $ow, $oh);
        $new_img = imagecreatetruecolor($this->_width, $this->_height);
        imagecopyresampled($new_img, $tmp_img, 0, 0, $offset_w, $offset_h, $this->_width, $this->_height, $this->_width, $this->_height);
        $this->save_image($new_img, $this->_dest);
        $this->add_watermark($this->_dest);
        return is_file($this->_dest);
    }

    /** 計算目標尺寸 */
    private function get_size() {
        list($ow, $oh) = getimagesize($this->_source);
        $w = (int)$this->_width; $h = (int)$this->_height;
        if ($this->_type == 'fit') {
            $pic_w = $w; $pic_h = (int)($pic_w * $oh / $ow);
            if ($pic_h > $h) { $pic_h = $h; $pic_w = (int)($pic_h * $ow / $oh); }
        } else { // crop
            if ($ow > $oh) { $pic_h = $h; $pic_w = (int)($pic_h * $ow / $oh); }
            else { $pic_w = $w; $pic_h = (int)($pic_w * $oh / $ow); }
        }
        return array($pic_w, $pic_h);
    }

    /** 計算裁剪偏移 */
    private function get_crop_offset($pic_w, $pic_h) {
        $ow = $oh = 0;
        switch (strtoupper($this->_croppos)) {
            case 'TL': $ow = 0; $oh = 0; break;
            case 'TM': $ow = (int)(($pic_w - $this->_width)/2); $oh = 0; break;
            case 'TR': $ow = $pic_w - $this->_width; $oh = 0; break;
            case 'ML': $ow = 0; $oh = (int)(($pic_h - $this->_height)/2); break;
            case 'MM': $ow = (int)(($pic_w - $this->_width)/2); $oh = (int)(($pic_h - $this->_height)/2); break;
            case 'MR': $ow = $pic_w - $this->_width; $oh = (int)(($pic_h - $this->_height)/2); break;
            case 'BL': $ow = 0; $oh = $pic_h - $this->_height; break;
            case 'BM': $ow = (int)(($pic_w - $this->_width)/2); $oh = $pic_h - $this->_height; break;
            case 'BR': $ow = $pic_w - $this->_width; $oh = $pic_h - $this->_height; break;
        }
        return array($ow, $oh);
    }

    /** 加浮水印 */
    private function add_watermark($dest) {
        if (!$this->_watermark || !file_exists($this->_watermark) || !file_exists($dest)) return;
        list($ow, $oh, $ot) = getimagesize($dest);
        list($ww, $wh, $wt) = getimagesize($this->_watermark);
        if ($ww > $ow || $wh > $oh) return;

        if ($this->_handler == 'imagemagick') {
            $cmd = sprintf("composite -gravity %s -geometry %s -dissolve %s '%s' %s %s",
                $this->_gravity, $this->_geometry, $this->_opacity, $this->_watermark, $dest, $dest);
            $this->to_log($cmd);
            exec($cmd);
        } else {
            // GD 水印(不支援透明度)
            $water_img = $this->create_source_image($this->_watermark, $wt);
            $dest_img = $this->create_source_image($dest, $ot);
            list($posX, $posY) = $this->calc_watermark_pos($ow, $oh, $ww, $wh);
            imagealphablending($dest_img, true);
            imagecopy($dest_img, $water_img, $posX, $posY, 0, 0, $ww, $wh);
            $this->save_image($dest_img, $dest);
            imagedestroy($water_img); imagedestroy($dest_img);
        }
    }

    /** 檢查處理引擎 */
    private function check_handler() {
        $im = (strstr(shell_exec('convert -version'), 'Version: ImageMagick') !== false);
        $gd = function_exists('gd_info');
        if ($this->_handler === 'imagemagick') return $im;
        if ($this->_handler === 'gd') return $gd;
        if ($im) { $this->_handler = 'imagemagick'; return true; }
        if ($gd) { $this->_handler = 'gd'; return true; }
        return false;
    }

    /** 輔助方法:建立來源圖片資源 */
    private function create_source_image($file, $type) {
        switch ($type) {
            case 1: return imagecreatefromgif($file);
            case 2: return imagecreatefromjpeg($file);
            case 3: return imagecreatefrompng($file);
            default: return false;
        }
    }

    /** 輔助方法:儲存圖片 */
    private function save_image($img, $dest) {
        $ext = $this->get_file_ext($dest);
        switch ($ext) {
            case 1: imagegif($img, $dest, $this->_quality); break;
            case 2: imagejpeg($img, $dest, $this->_quality); break;
            case 3: imagepng($img, $dest, (int)(($this->_quality - 1) / 10)); break;
        }
    }

    /** 輔助方法:計算水印位置 */
    private function calc_watermark_pos($ow, $oh, $ww, $wh) {
        switch (strtolower($this->_gravity)) {
            case 'northwest': return array(0, 0);
            case 'north': return array(($ow - $ww) / 2, 0);
            case 'northeast': return array($ow - $ww, 0);
            case 'west': return array(0, ($oh - $wh) / 2);
            case 'center': return array(($ow - $ww) / 2, ($oh - $wh) / 2);
            case 'east': return array($ow - $ww, ($oh - $wh) / 2);
            case 'southwest': return array(0, $oh - $wh);
            case 'south': return array(($ow - $ww) / 2, $oh - $wh);
            case 'southeast': return array($ow - $ww, $oh - $wh);
        }
        return array(0, 0);
    }

    /** 其他輔助方法:create_dirs, exists, to_log, hex2rgb, get_file_ext... */
}
?>

使用範例

<?php
require('PicThumb.class.php');
$logfile = __DIR__ . '/PicThumb.log';
$source = __DIR__ . '/pic/source.jpg';
$watermark = __DIR__ . '/pic/watermark.png';

// 1. 等比縮圖 100x100
$obj = new PicThumb($logfile);
$obj->set_config(array('type'=>'fit', 'width'=>100, 'height'=>100));
$obj->create_thumb($source, __DIR__ . '/pic/1.jpg');

// 2. 等比縮圖 + 紅色背景填充
$obj->set_config(array('type'=>'fit', 'width'=>100, 'height'=>100, 'bgcolor'=>'#FF0000'));
$obj->create_thumb($source, __DIR__ . '/pic/2.gif');

// 3. 裁剪 250x250 (底部置中) + 水印 (西南角, 透明度 50%)
$obj->set_config(array(
    'type'=>'crop', 'croppos'=>'BM', 'width'=>250, 'height'=>250,
    'watermark'=>$watermark, 'opacity'=>50, 'gravity'=>'SouthWest'
));
$obj->create_thumb($source, __DIR__ . '/pic/3.png');

// 4. CMYK 圖片自動轉 RGB 處理
$source_cmyk = __DIR__ . '/pic/source_cmyk.jpg';
$obj->set_config(array('type'=>'fit', 'width'=>100, 'height'=>100));
$obj->create_thumb($source_cmyk, __DIR__ . '/pic/4.jpg');
?>

選擇建議

  • ImageMagick:生產環境首選,效能高、支援格式多、處理大圖記憶體效率佳
  • GD:虛擬主機無法安裝 ImageMagick 時的備選,或簡單專案
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved