用php将动态gif拆分成单帧


我想把一个动态gif拆分成一帧一帧的 然后对单帧进行修改后再合并起来 在网上找了一段代码 但是在拆分成单帧的时候就出了问题 但是不知道错在哪里了 请各位大虾帮忙看看 代码如下


 <?php

class TGif {
  var $signature; //签名
  var $version;  //版本
  var $image = array();
  var $frame = 0;
  var $buffer = ''; //图片数据缓存区

  /**
   * 内部方法 next
   * 从图片数据缓冲区读取指定长度的字符串
   * 参数 $len 数值,读取的长度,确省为1
   * 返回 长度为1时返回码值
   *      其他返回字符串
   **/
  function next($len=1) {
    $ch = substr($this->buffer, 0, $len);
    $this->buffer = substr($this->buffer, $len);
    if($len == 1)
        return ord($ch);
    return $ch;
  }

  /**
   * 内部方法 get_number
   * 从图片数据缓冲区读取一个整数
   * 参数 无
   * 返回 整数
   **/
  function get_number() {
    $t = unpack("Sn", $this->next(2));
    return $t['n'];
  }

  /**
   * 构造函数
   * 参数 $imagename 字符串,图片文件名或图片数据
   * 返回 无
   **/
  function TGif($imagename="images/xzn.gif") {
    $this->image = array();
    $this->frame = 0;
    if(is_file($imagename)) {
        $this->imagename = $imagename;
        $this->buffer = @file_get_contents($imagename)
            or trigger_error("$imagename 打不开", E_USER_ERROR);
    }else {
        substr($imagename, 0, 3) == 'GIF' or
            trigger_error("$imagename 不存在或不是GIF格式图片", E_USER_ERROR);
        $this->buffer = $imagename;
        $this->imagename = '未命名';
    }
    $this->get_header();
    while(strlen($this->buffer)) {
        switch($this->next()) {
            case 0x21: //图象扩展描述符
                $this->get_extension_introducer();
                break;
            case 0x2c: //图象描述符
                $this->get_image_descriptor();
                break;
            case 0x3b: //GIF数据流结束
                return;
        }
    }
  }

  /**
   * 内部方法 get_color_table
   * 读取调色板数据
   **/
  function get_color_table($num) {
    return $this->next(3*pow(2,$num+1));
  }

  /**
   * 内部方法 get_header
   * 读取头信息
   **/
  function get_header() {
    $this->signature = $this->next(3);    //类型标识gif
    if($this->signature != 'GIF')
        trigger_error("$this->imagename 不是GIF格式图片", E_USER_ERROR);
    $this->version = $this->next(3);    //版本标识
    $this->logical_screen_width = $this->get_number();    //逻辑屏幕宽
    $this->logical_screen_height = $this->get_number();    //逻辑屏幕高
    $this->flag = $flag = $this->next();
    $this->global_color_table_flag = ($flag & 0x80) > 0;    //是否有全局调色板
    $this->color_resolution = (($flag >> 4) & 0x07) + 1;    //彩色分辨率
    $this->sort_flag = ($flag & 0x08) > 0;            //调色板是否排序
    $this->size_of_global_color_table = $flag & 0x07;    //全局色板大小,2的乘方的指数
    $this->background_color_index = $this->next();        //背景色索引
    $this->pixel_aspect_ratio = $this->next();         //像素纵横比
    if($this->global_color_table_flag)
        $this->global_color_table = $this->get_color_table($this->size_of_global_color_table);
  }

  /**
   * 内部方法 get_image_descriptor
   * 读取图片信息
   **/
  function get_image_descriptor() {
    $image['left_position'] = $this->get_number();
    $image['top_position'] = $this->get_number();
    $image['width'] = $this->get_number();
    $image['height'] = $this->get_number();
    $image['flag'] = $flag = $this->next();
    $image['local_color_table_flag'] = ($flag & 0x80) > 0;    //局部色表
    $image['interlace_flag'] = ($flag & 0x40) > 0;        //交错
    $image['local_sort_flag'] = ($flag & 0x20) > 0;        //色表是否排序
    $image['size_of_local_color_table'] = $flag & 0x07;    //局部色表大小
    if($image['local_color_table_flag'])
        $image['local_color_table'] = $this->get_color_table($image['size_of_local_color_table']);
    $image['data'] = $this->get_table_based_image_data();
    $this->image[$this->frame] = array_merge($this->image[$this->frame], $image);
    $this->frame++;
  }

  /**
   * 内部方法 get_table_based_image_data
   * 读取图象数据
   **/
  function get_table_based_image_data() {
    $table_based_image_data_size = 0;
    $table_based_image_data = chr($this->next());
    while($n = $this->next()) {
        $table_based_image_data_size += $n;
        $table_based_image_data .= chr($n);
        $table_based_image_data .= $this->next($n);
    }
    $table_based_image_data .= chr(0);
    return $table_based_image_data;
  }

  /**
   * 内部方法 get_extension_introducer
   * 读取图象扩展
   **/
  function get_extension_introducer() {
    switch($this->next()) {
        case 0xf9:
            $size = $this->next(); //固定为4
            $flag = $this->next();
            $this->image[$this->frame]['disposal_method'] = ($flag >> 2) & 0x07;
            $this->image[$this->frame]['transparent_flag'] = $flag & 0x01;
            $this->image[$this->frame]['delay_time'] = $this->get_number();
            $this->image[$this->frame]['transparecy_index'] = $this->next();
            $this->next();
        break;
    case 0xfe:
        while($this->next() != 0);
        break;
    case 0x01:
        $this->next();
        $delay_time = $this->get_number();
        $delay_time = $this->get_number();
        $delay_time = $this->get_number();
        $delay_time = $this->get_number();
        $this->next();
        $this->next();
        $this->next();
        $this->next();
        while($this->next() != 0);
        break;
    case 0xff:
        $this->application_extension = $this->next($this->next());
        while($this->next() != 0);
        break;
    }
  }

  /**
   * 公共方法 info
   * 产生图片信息报告
   **/
  function info() {
    if(isset($_GET['frame'])) {
        echo $this->withdraw($_GET['frame']);
        exit;
    }
    $dict = array(
        'logical_screen_width' => '图片宽度',
        'logical_screen_height' => '图片高度',
        'global_color_table_flag' => '全局色表',
        'color_resolution' => '彩色分辨率',
        'sort_flag' => '排序标志',
        'size_of_global_color_table' => '全局色表大小',
        'background_color_index' => '背景色索引',
        'pixel_aspect_ratio' => '像素纵横比',
        'frame' => '帧数',
        'application_extension' => '应用程序扩展',
        );
    $image_dict = array(
        'left_position' => '图象左边距',
        'top_position' => '图象上边距',
        'width' => '图象宽',
        'height' => '图象高',
        'local_color_table_flag' => '局部色表',
        'interlace_flag' => '交错',
        'local_sort_flag' => '排序标志',
        'size_of_local_color_table' => '局部色表大小',
        'delay_time' => '停顿时间',
        'disposal_method' => '处置方式',
        'transparecy_index' => '透明色索引',
        );
    echo '<table border>';
    printf("<tr><th colspan=2>图片文件名 %s</th></tr>", $this->imagename);
    echo '<tr><td><table>';
    foreach($dict as $key => $value)
        printf("<tr><td>%s</td><td>%s</td></tr>",$value,$this->$key);
    printf("</table></td><td><img src='%s'></td></tr>", $this->imagename);
    for($i=0; $i<$this->frame; $i++) {
        printf("<tr><th colspan=2>帧号 %s</th></tr>",$i);
        echo '<tr><td><table>';
        foreach($image_dict as $key => $value)
            printf("<tr><td>%s</td><td>%s</td></tr>",$value,$this->image[$i][$key]);
        printf("</table></td><td><img src='?frame=%d'></td></tr>", $i);
    }
    echo "</table>";
  }

  /**
   * 内部方法 control_extension,由withdraw方法调用
   * 输出图象扩展控制段
   * 参数
   *  $frame 数值,图片帧号
   *  $delay_time 数值,图象停顿时间,单位为百分秒(10ms)
   *  $disposal_mothod 数值,处置方式
   * 返回 无
   **/
  function control_extension($frame=0, $delay_time=10, $disposal_mothod=0) {
    $transparent = $this->image[$frame]['transparecy_index'];
    $flag = ($disposal_mothod << 2) | $this->image[$frame]['transparent_flag'];
    echo pack("CCCCSCC", 0x21, 0xf9, 4, $flag, $delay_time, $transparent, 0);
  }

  /**
   * 内部方法 image_frame,由withdraw方法调用
   * 输出一帧图象
   * 参数
   *  $frame 数值,图片帧号
   *  $left 数值,图象左边距
   *  $top 数值,图象上边距
   *  $colortab 字符串,对比用的全局调色板数据
   * 返回 无
   **/
  function image_frame($frame=0, $left=null, $top=null, $colortab=null) {
      if($left == null) $left = $this->image[$frame]['left_position'];
      if($top == null) $top = $this->image[$frame]['top_position'];
    $flag = $this->image[$frame]['flag'];
    $color_table = '';
    if($this->image[$frame]['local_color_table_flag']) {
        //如图象有局部调色板则取局部调色板
        $color_table = $this->image[$frame]['local_color_table'];
    }elseif($colortable != null && $colortable != $this->global_color_table) {
        //如图象没有局部调色板且全局调色板与对比调色板不同,就把全局调色板设置为局部调色板
        $flag &= 0x40; //保留交错标志
        $flag |= $this->sort_flag ? 0x20 : 0x00; //设置排序标志
        $flag |= 0x80; //设置局部色表标志
        $flag |= $this->size_of_global_color_table;
        $color_table = $this->global_color_table;
    }
    $width = $this->image[$frame]['width'];
    $height = $this->image[$frame]['height'];
    echo pack("CSSSSC", 0x2c, $left, $top, $width, $height, $flag);
    echo $color_table;
    echo $this->image[$frame]['data'];
  }

  /**
   * 内部方法 image_header,由withdraw方法调用
   * 输出图片头
   * 参数
   *  $width 数值,图片宽
   *  $height 数值,图片高
   * 返回 无
   **/
  function image_header($width=0, $height=0) {
    ob_start();
      printf("GIF89a%s%s%c%c%c"
          , pack("S", $this->logical_screen_width)
          , pack("S", $this->logical_screen_height)
          , $this->flag
          , $this->background_color_index
          , $this->pixel_aspect_ratio
          );
    if($this->global_color_table_flag)
        echo $this->global_color_table;
  }

  /**
   * 内部方法 image_end,由withdraw方法调用
   * 输出图片尾,并返回图片数据
   * 返回 字符串,图片数据
   **/
  function image_end() {
    echo chr(0x3b);
    $buf = ob_get_clean();
    return $buf;
  }

  /**
   * 公共方法 withdraw
   * 生成单帧的图片[文件]
   * 参数
   *  $frame 数值,提取的帧号
   *  $filename 字符串,目标文件名,缺省为空(不生成文件)
   * 返回 字符串,图片数据
   **/
  function withdraw($frame=0, $filename='') {
    if($frame > $this->frame-1) $frame = 0;
    $this->image_header();
    $this->control_extension($frame);
    $this->image_frame($frame);
    $buf = $this->image_end();
    if(!empty($filename))
         file_put_contents($filename, $buf);
    return $buf;
  }
}


$p = new TGif('old.gif');
//$p->info();
echo $p->withdraw(0, 'hello.gif');
//print_r($p->image);
?>

gif php

slllll 10 years, 3 months ago

使用 imagemagick就很简单了

种田东山上 answered 10 years, 3 months ago

try imagemagick

被封印的ID answered 10 years, 3 months ago

Your Answer