2/30, 235«12345»

PHP SWF Reader

2010-9-30 11:50:00 网摘 抢沙发(1)

 File_SWF which inherits from PEAR package, allows operations I/O on swf files. 

Basically, other than returning all the main information from an swf file, such as the bg color, framerate, player version, compression, protection used, movie size.. it allows to modify some of those parameters.
For example it can change the framerate, background, protection.

 

An Example:

<?php

// include the PEAR package

require "File/SWF.php";

$flash = new SWF("source.swf");

if($flash->is_valid()){ $stat = $flash->stat();

// this give all the info // and also..

$fps = $flash->getFrameRate();

$size = $flash->getMovieSize();

$bg = $flash->getBackgroundColor();

$prot = $flash->getProtected();

$compr = $flash->getCompression();

$version = $flash->getVersion();

// they can be changed...

$flash->setFrameRate(60);

$flash->setProtected(1);

$flash->setBackgroundColor(51,51,204);

$flash->setCompression(1);

// and write a new file...

$flash->write("./new_one.swf",1); }

?>

 

 

Here the File source

<?php

/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Original Author <alessandro@sephiroth.it> |
// +----------------------------------------------------------------------+


/**
* required base PEAR package
*/
require_once "PEAR.php";
/**
* Required package File for I/O operations
*/
require_once "File.php";

/**
* error constant
*/
define('PEAR_SWF_ID_ERR'1);

/**
* Base SWF class
*
* This class requires the File PEAR.
* Read the SWF header informations and return an
* associative array with the property of the SWF File, the result array
* will contain framerate, framecount, background color, compression, filetype
* version and movie size.
* <code>
* <?php
* require_once "File/File_SWF.php";
* $file = "any_file.swf";
*
* $swf = &new File_SWF($file);
* if($swf->isValid){
* $result = $swf->stat();
* print_r($result);
* }
* <?
* </code>
* @author Alessandro Crugnola <alessandro@sephiroth.it>
* @access public
* @version 0.2
* @package File_SWF
*/
class File_SWF
{
    
/**
    * current unpacked binary string
    * @var mixed
    */
    
var $current "";
    
/**
    * internal pointer
    * @var integer
    */
    
var $position 0;
    
/**
    * use zlib compression
    * @var boolean
    */
    
var $compression 0;
    
/**
    * current position
    * @var integer
    */
    
var $point 0;
    
/**
    * is a valid swf
    * @var boolean
    * @access private
    */
    
var $isValid 0;
    
/**
    * stirng file name to parse
    * @var string
    */
    
var $file "";
    
/**
    * determine if file is protected
    * @var boolean
    */
    
var $protected false;
    
/**
    * password for protected files
    * @var mixed
    */
    
var $password;

    
/**
    * Deconstructor
    * does anything right now
    * @access public
    */
    
function _File_SWF()
    {
    }

    
/**
    * Costructor
    * creates a new SWF object
    * reads the given file and parse it
    * @param string $file file to parse
    * @access public
    */
    
function File_SWF($file="")
    {
        
$this->compression 0;
        
$this->isValid 0;
        
$this->point 0;
        
$this->file $file;
        
$head File::read($this->file3);
        if(
PEAR::isError($head)){
            return 
$head;
        }
        
File::rewind($this->file"rb");
        if(
$head == "CWS"){
            
$data File::read($this->file8);
            
$_data gzuncompress(File::read($this->filefilesize($this->file)));
            
$data $data $_data;
            
$this->data $data;
            
$this->compression 1;
            
$this->isValid 1;
        } else if (
$head == "FWS"){
            
$this->data File::read($this->filefilesize($this->file));
            
$this->isValid 1;
        } else {
            
/**
            * invalid SWF file, or invalid head tag found
            */
            
$this->isValid 0;
        }
        
File::close($this->file"rb");
    }

    
/**
    * Is a valid SWF file
    * @return boolean
    * @access public
    */
    
function is_valid()
    {
        return 
$this->isValid;
    }

    
/**
    * Return if swf file is protected from import
    * @return boolean
    * @access public
    */
    
function getProtected()
    {
        if(
$this->getVersion() >= 8){
            
$this->_seek(31);
        } else {
            
$this->_seek(26);
        }
        
$data $this->_readTag();
        
$tag  $data[0];
        
$this->protected $tag == 24;
        return 
$this->protected;
    }

    
/**
    * Define import protection for the SWF
    * @param boolean $protect define is file must be protected
    * @access public
    */
    
function setProtected($protect)
    {
        if(
$protect and !$this->protected){
            if(
$this->getVersion() >= 8){
                
$pre substr($this->data031);
                
$post substr($this->data31);
            } else {
                
$pre substr($this->data026);
                
$post substr($this->data26);
            }
            
$middle pack("v"1536);
            
$this->data $pre $middle $post;
            
$this->password 0;
            
$this->protected true;
        } else if(!
$protect and $this->protected){
            if(
$this->getVersion() >= 8){
                
$pos 31;
            } else {
                
$pos 26;
            }
            
$this->_seek($pos);
            if(
$this->_readData()){
                
$this->data substr($this->data,0$pos) . substr($this->data$this->point - (is_string($this->password) == 1));
            }
        }
    }

    
/**
    * Return the current SWF frame rate
    * @return mixed interger frame rate in fps or Error if invalid file
    * @access public
    */
    
function getFrameRate()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        if(
$this->getVersion() >= 8){
            
$this->_seek(16);
        } else {
            
$this->_seek(17);
        }
        
$fps unpack('vrate',$this->_read(2));
        return 
$fps['rate']/256;
    }

    
/**
    * Set the new Frame Rate
    * @access public
    */
    
function setFrameRate($num)
    {
        if(!
$this->is_valid()){
            return;
        }
        
$num intval($num);
        if(
$num and $num <= 120){
            
$fps pack('v'$num*256);
            if(
$this->getVersion() >= 8){
                
$this->_seek(16);
                
$this->data substr($this->data016) . $fps substr($this->data18);
            } else {
                
$this->_seek(17);
                
$this->data substr($this->data017) . $fps substr($this->data19);
            }
        }
    }

    
/**
    * Return the current number of frames
    * @return mixed interger or error if invalid file format
    * @access public
    */
    
function getFrameCount()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        if(
$this->getVersion() >= 8){
            
$this->_seek(18);
        } else {
            
$this->_seek(19);
        }
        return 
$this->_readshort();
    }

    
/**
    * Return the current movie size in pixel
    * @return mixed array or error if invalid file format
    * @access public
    */
    
function getMovieSize()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        
$this->_seek(8);
        return 
$this->_readRect();
    }

    
/**
    * Return the current file type (CWS, FWS)
    * @return mixed string or error if invalid file format
    * @access public
    */
    
function getFileType()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        
$this->_seek(0);
        return 
$this->_read(3);
    }

    
/**
    * Return the current compression used
    * @return mixed interger or error if invalid file format
    * @access public
    */
    
function getCompression()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        return 
$this->compression;
    }

    
/**
    * Set the compression
    * @return string based on the compression used
    * @param integer $mode compression on/off
    * @access public
    */
    
function setCompression($mode 0)
    {
        if(!
$this->is_valid()){
            return;
        }
        
$data "";
        
$real_size pack"V"strlen($this->data));
        
$this->data substr($this->data04) . $real_size substr($this->data8strlen($this->data));
        if(
$mode == 0){
            
$this->compression 0;
            
$this->data "FWS" substr($this->data3);
            
$_n1 substr($this->data08);
            
$_n2 substr($this->data8strlen($this->data));
            
$data $_n1 $_n2;
        } else if(
$mode == 1){
            
$this->compression 1;
            
$this->data "CWS" substr($this->data3);
            
$_n1 substr($this->data08);
            
$_n2 substr($this->data8strlen($this->data));
            
$_n3 gzcompress($_n2);
            
$data $_n1 $_n3;
        }
        return 
$data;
    }

    
/**
    * Return the current version of player used
    * @return mixed interger or error if invalid file format
    * @access public
    */
    
function getVersion()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        
$this->_seek(3);
        return 
$this->_readbyte();
    }

    
/**
    * Return the current SWF file size
    * @return mixed interger or error if invalid file format
    * @access public
    */
    
function filesize()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        
$this->_seek(4);
        
$real_size unpack"Vnum"$this->_read(4) );
        if( 
$this->getCompression() ){
            
$n $this->data;
            
$n "CWS" substr($n38) . gzcompress(substr($n8strlen($n)));
            
$file_size strlen$n ) -3;
        } else {
            
$file_size strlen$this->data )-3;
        }
        return array(
$file_size$real_size['num'], "compressed" => $file_size"real" => $real_size['num']);
    }

    
/**
    * Return the current background color
    * @return mixed array or error if invalid file format
    * @access public
    */
    
function getBackgroundColor()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        if(
$this->getVersion() >= 8){
            
$this->_seek(26);
        } else {
            
$this->_seek(21);
        }
        return 
$this->_readData();
    }

    
/**
    * Set the new background color
    * @param integer $r (0,255)
    * @param integer $g (0,255)
    * @param integer $b (0,255)
    * @access public
    */
    
function setBackgroundColor($r=0$g=0$b=0)
    {
        if(!
$this->is_valid()){
            return;
        }
        
$color  pack("C"$r);
        
$color .= pack("C"$g);
        
$color .= pack("C"$b);

        if(
$this->getVersion() >= 8){
            
$data substr($this->data028);
            
$data .= $color;
            
$this->data $data substr($this->data31strlen($this->data));
        } else {
            
$data substr($this->data023);
            
$data .= $color;
            
$this->data $data substr($this->data26strlen($this->data));
        }
    }

    
/**
    * Save current swf as a new file
    * @param string $filename filename
    * @param boolean $overwrite overwrite existing file
    * @return boolean true if saved succesfully
    * @access public
    */
    
function write($filename$overwrite 1)
    {
        if(!
$this->is_valid()){
            return 
false;
        }
        if(
is_writeable(dirname($filename))){
            if(
is_file($filename)){
                if(
$overwrite == 0){
                    return 
false;
                }
            }
            
$newdata $this->setCompression($this->getCompression());
            
File::write ($filename$newdata$mode "wb");
            
File::close($filename"wb");
            return 
true;
        } else {
            return 
false;
        }
    }

    
/**
    * reads the SWF header
    * @return mixed associative array or error on fault
    * @access private
    */
    
function stat()
    {
        if(!
$this->is_valid()){
            return 
PEAR::raiseError("Invalid SWF head TAG found in " $this->filePEAR_SWF_ID_ERR);
        }
        
$filetype $this->getFileType();
        
$version $this->getVersion();
        
$filelength $this->filesize();
        
$rect $this->getMovieSize();
        
$framerate $this->getFrameRate();
        
$framecount $this->getFrameCount();
        
$background $this->getBackgroundColor();
        
$protection $this->getProtected();
        return array(
            
"zlib-compression" => $this->getCompression(),
            
"fileType" => $filetype,
            
"version" => $version,
            
"fileSize" => $filelength,
            
"frameRate" => $framerate,
            
"frameCount" => $framecount,
            
"movieSize" => $rect,
            
"background" => $background,
            
"protected" => $protection,
        );
    }

    
/**
    * read tag type, tag length
    * @return array
    * @access private
    */
    
function _readTag()
    {
        
$n $this->_readshort();
        if(
$n == 0)
        {
            return 
false;
        }
        
$tagn $n>>6;
        
$length $n&0x3F;
        if(
$length == 0x3F)
        {
            
$length $this->_readlong();
        }
        return array(
$tagn,$length);
    }

    
/**
    * read long
    * @access private
    */
    
function _readlong(){
        
$ret unpack("Nnum"$this->_read(4));
        return 
$ret['num'];
    }

    
/**
    * read data of next tag
    * @return array
    * @access private
    */
    
function _readData()
    {
        
$tag $this->_readTag();
        
$tagn $tag[0];
        
$length $tag[1];
        if(
$tagn == 9)
        {
            
$r $this->_readbyte();
            
$g $this->_readbyte();
            
$b $this->_readbyte();
            
$data = array($r,$g,$b"hex" => sprintf("#%X%X%X"$r$g$b));
            return 
$data;
        } else if(
$tagn == 24)
        {
            if(
$this->_readbyte() == 0x00){
                
$this->_readbyte();
                
$this->password $this->_readstring();
            } else {
                
$this->password 0;
            }
            return 
true;
        }
        return array();
    }

    
/**
    * read a string
    * @return string
    * @access private
    */
    
function _readstring()
    {
        
$s "";
        while(
true){
            
$ch $this->_read(1);
            if(
$this->point strlen($this->data)){
                break;
            }
            if(
$ch == "\x00"){
                break;
            }
            
$s .= $ch;
        }
        return 
$s;
    }


    
/**
    * read internal data file
    * @param integer $n number of byte to read
    * @return array
    * @access private
    */
    
function _read($n)
    {
        
$ret substr($this->data$this->point$n);
        
$this->point += $n;
        return 
$ret;
    }

    
/**
    * move the internal pointer
    * @param integer $num
    * @access private
    */
    
function _seek($num){
        if(
$num 0){
            
$num 0;
        } else if(
$num strlen($this->data)){
            
$num strlen($this->data);
        }
        
$this->point $num;
    }

    
/**
    * read short
    * @return string
    * @access private
    */
    
function _readshort(){
        
$pack unpack('vshort',$this->_read(2));
        return 
$pack['short'];
    }

    
/**
    * read single byte
    * @return string
    * @access private
    */
    
function _readByte(){
        
$ret unpack("Cbyte",$this->_read(1));
        return 
$ret['byte'];
    }
    
/**
    * read a rect type
    * @return rect
    * @access private
    */
    
function _readRect(){
        
$this->_begin();
        
$l $this->_readbits(5);
        
$xmin $this->_readbits($l)/20;
        
$xmax $this->_readbits($l)/20;
        
$ymin $this->_readbits($l)/20;
        
$ymax $this->_readbits($l)/20;
        
$rect = array(
            
$xmax,
            
$ymax,
            
"width" => $xmax,
            
"height" => $ymax
        
);
        return 
$rect;
    }

    
/**
    * read position internal to rect
    * @access private
    */
    
function _incpos(){
        
$this->position += 1;
        if(
$this->position>8){
            
$this->position 1;
            
$this->current $this->_readbyte();
        }
    }
    
/**
    * read bites
    * @param integer $nbits number of bits to read
    * @return string
    * @access private
    */
    
function _readbits($nbits){
        
$n 0;
        
$r 0;
        while(
$n $nbits){
            
$r = ($r<<1) + $this->_getbits($this->position);
            
$this->_incpos();
            
$n += 1;
        }
        return 
$r;
    }

    
/**
    * getbits
    * @param integer $n
    * @return long
    * @access private
    */
    
function _getbits($n){
        return (
$this->current>>(8-$n))&1;
    }

    
/**
    * begin reading of rect object
    * @access private
    */
    
function _begin(){
        
$this->current $this->_readbyte();
        
$this->position 1;
    }
}

 

 

 

 

AJBridge重生

2010-9-2 20:44:07 AJBridge 抢沙发(0)

 

经过1个多月的努力,AJBridge终于重生了,说起这个还得感谢玉伯同学的 KISSY 和他 诚挚的帮助。

说实话,“不当爹的不知道当爹的苦”这句话我可是深度体会了。

文档:

     AJBridge:http://kissyteam.github.com/kissy-ajbridge/

     KISSY.Flash: http://kissyteam.github.com/kissy/docs/index.html

历史:

2010年09月02日  AJBridge发布 。 ver. release 1.0

                                同时完成 AJBridge 和 Store 的 API文档  http://kissyteam.github.com/kissy-ajbridge/ 

2010年09月01日  AJBridge 预发布。ver: 1.0.12   。

                                同时发布 flash 本地存储库  Store。

2010年08月26日  AJBridge review。并依据最新的 KISSY库特性进行了重构。

                                 同日,Kissy-AJBridge库开始正式运作。

                                  至此 AJBridge 正式成为 KISSY的继 KISSY editor 子项目的另一项子品牌。

2010年08月25日  经陆陆续续的空余时间,终于将新人指引文档完成。  

2010年08月16日  开始撰写KISSY.Flash指引文档

2010年08月03日  KISSY  1.1版发布(已包含 KISSY.Flash)。

                                 同日,基于该版本的 KISSY.Flash 和 AJBridge 以及 SWFStore 重新撰写 并加入 标准测试页。

2010年08月02日  撰写完成 KISSY.Flash API文档。

2010年07月27日  完成可运行的KISSY.AJBridge

2010年07月26日  开始着手写KISSY.AJBridge

2010年07月25日  KISSY迁移至github安家。同时,确定了 AJBridge在 KISSY中所扮演的角色,见玉伯的《KISSY 迁移到 github.com

2010年07月23日  完成可运行的KISSY.Flash

2010年07月21日   开始着手写KISSY.Flash 

2010年06月30日  尝试应用AJBridge的新白名单机制至SWFStore中。

                                 由于YUI的SWFStore还在beta阶段,并且有一些bug。

                                 考虑到后期维护以及和之前版本统一及合并的问题最终决定和KISSY的初衷一样,自己来创建。                       

2010年06月29日  增加了AJBridge的AS部分的白名单机制,借鉴了 YUI SWFStore的 白名单机制。

2010年03月18日  某些原因接触了 公司某WEB IM 的 flash相关部分业务。接触了YUI 的SWFStore.

                                 意外的发现代码部分如此相似,似乎印证了本人“一个弹坑可以落数个炮弹”的结论。

                                 同月参加“CM”项目,虽然竞标项目失败,但是从此接触了“KISSY”。

2010年01月04日  应用到公司的一个内部项目,代号“HYQ”

                                 期间发现并修复了一些代码

。。。                    孤独......莫非快死掉了么.........

2009年11月19日  增加了demo,并修改代码,支持多事件多swf。

2009年11月11日  修复一些bug,优化了代码 ver.r3。

2009年11月10日  修复一些bug。ver.r2。

2009年11月06日   第一版完整代码的 AJBridge 诞生,并上传至 google code 的 AJBridge项目

                                  同时又撰写了一篇博文做记录

2009年11月05日    决定使用 google code 进行代码管理。

2009年10月28日    决定开始编写 AJBridge。目的仅仅是封装AS和JS通信的公用部分,减少工作量。

 

 

KISSY.Flash开发札记---进化

2010-8-5 16:26:51 AJBridge 抢沙发(0)

 说之前先说下KISSY.Flash的任务目标:

  • 支持多SWF管理。
  • 支持动态、静态两种方式获取SWF。
  • 对第三方参数 flashvars的值进行有向过滤(动态)。
  • 支持第三方参数flashvars的内容,深度大于1。 
  • 支持第三方参数flashvars深度大于1的内容JSON串化。
  • 支持在线安装最新的flash播放器。
  • 对不支持开发者期望版本的客户端提供具体的错误参考。

再说说这个库的构成。

  • flash.js                              对KISSY抽象资源做了约定,便于未来的更改升级,并确立了 KISSY.Flash的命名空间。
  • flash-embed.js               当前 KISSY.Flash 具体逻辑和功能的实现主体。
  • flash-ua.js                        针对KISSY.UA的扩展,提供对客户flash播放器版本判断以及开发者期望运行版本检测。

此次主要说的是第一条关于SWF多条管理的抽象资源   flash.js

看第一次代码:

KISSY.add('flash', function(S){

S.Flash = {

instances: { }, //存储每个已获取到的 swf 的 html元素

archives: []     //存储每个swf的id

};

});

当初这样的设计是为了能对 swf进行通过序列操作,并能获得附加SWF个数统计,以及快速的对象获取。

而且当初设定了 KISSY.Flash 就是个静态对象,因此对 swf个数的获取需要动态的。

而之后的从简单和容易记的角度上来考虑,于是觉得 用 swfs 代替 instances和 names 代替 archives 更好的从字面理解存在的意义和便于记忆。

KISSY.add('flash', function(S){

S.Flash = {

swfs: { },

names: 

};

});

后来,玉伯同学说既然获取/操作swf都需要id,且并以此作为swfs的key,不如就直接将names称为ids。

看上去是个好主意,于是又有了

KISSY.add('flash', function(S){

S.Flash = {

swfs: { },

ids:[]

};

});

心想,这应该完美了吧,但事实上世界本就无完美,如人无完人一样。

在实际操作过程中真正通过 index搜寻 swf的用途很少,而开发人员往往在一开始就可以预定id。

KISSY的原则就是2/8原则,只挑最好的,不挑最少的。

因此再三考虑决定废弃 ids这个数组对象。

但是问题来了,没有了数组,我们就无法动态获取SWF个数,而具体的实现要在后续的  flash-embed.js中实现。

于是根据抽象资源提供基本接口的约定,因此决定向swfs中加入length属性,通过 后续的增删操作提供增减操作。

而当时想到的是  swfs既然是个类 list 应该具备 length 属性,于是又有了以下代码:

KISSY.add('flash', function(S){

S.Flash = {

swfs: {length: 0 }

};

但是,总觉得这样写风险还是很大,比如某个 swf的 id 就叫 length。于是在存储规则中增加了一条规则:每个id都具有 “#”前缀。

这样就不会有污染,而且ID前带 “#”符合了 KISSY的DOM 选择器规则。这看起来很完美了。

可实际在做KISSY.Flash扩展的时候,问题又来了,由于swfHTML元素的id决定了as调用swf 唯一性,而这样的更改必然会对后续的操作带来一些工作。

比如正则处理ID。

但这样做又违背了 KISSY中的 一个S ------简单。

于是基于以下两点考虑最终让此静态抽象资源对象走向了最“幸福”的一站:

  • swfs应该是个池,而且是应是个纯粹的“一一对应池”。
  • 既然Flash就是个静态抽象资源对象,且管理多个实例,其本身也类似  collection ,更何况后续会有  add/remove/get,因此应该用最方便最易解的length。

于是。。。。事情就是那么简单。。。。。简单就是美嘛。。。。

KISSY.add('flash', function(S){

S.Flash = {

swfs: { },

length: 0

};

这样的好处可以直接通过  S.Flash.length获取到 Flash的个数。而之前则为了达成 获取 Flash中 SWF的个数采用了 length()或len()去获取S.Flash.swfs.length。

 

总结:

      事实上,我们在做开发的时候应该遵从第一直觉,既为了达成某项目标,其惯例或者说约定成俗的方式是什么。而理性往往不一定是对的,就好像对swf的个数统计就应该源于 swfs。但事实上作为使用者来说,更倾向于“我给谁了就向谁拿”的习惯,因此基于这2点 将 length作为属性直接写在静态抽象资源类上更适合不过了。

 

下一篇我将会隆重的介绍  KISSY.Flash 具体使用方式和过程。

 

KISSY Flash源码   :http://github.com/kissyteam/kissy/tree/master/src/flash/

KISSY Flash文档  : http://kissyteam.github.com/kissy/docs/flash/index.html 

 


 

好消息, OpenGL 4.1规格最后被确定,为手机和网页简化了三维图形显示

2010-7-27 16:59:08 网摘 抢沙发(0)

 

 

猜想:相信即将出来的flash player 11或者之后版本,以及随之的sdk将支持webgl 同时按oGL4.0规格。

 

2/30, 235«12345»