首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将32位TGA转换为PNG

将32位TGA转换为PNG
EN

Stack Overflow用户
提问于 2014-07-12 02:44:25
回答 2查看 1.2K关注 0票数 5

我是从这段代码工作,并考虑到评论,以“修复”32位,但它似乎仍然不起作用。我很确定这和TGA描述符有关。这将有位0-3作为阿尔法通道深度,这将始终是8的32位,而代码没有考虑到这一点。

我试着理解如何使用这个C代码作为向导将其拼凑在一起,但没有运气。

一旦考虑到像素长度为4(按照注释中的补丁程序),他的dwordize只占4个字节中的3个,第4个字节是我认为的alpha位。

我尝试将函数更改为

代码语言:javascript
运行
复制
function dwordize($str)
{
    $a = ord($str[0]);
    $b = ord($str[1]);
    $c = ord($str[2]);
    return $c*256*256 + $b*256 + $a;
}

代码语言:javascript
运行
复制
function dwordize($str)
{
    $a = ord($str[0]);
    $b = ord($str[1]);
    $c = ord($str[2]);
    $d = ord($str[3]);
    return $d*256*256*256 + $c*256*256 + $b*256 + $a;
}

不起作用,然后试着

代码语言:javascript
运行
复制
function dwordize($str)
{
    $a = ord($str[0]);
    $b = ord($str[1]);
    $c = ord($str[2]);
    $d = ord($str[3]);
    return $c*256*256 + $b*256 + $a + $d*256*256*256;
}

所有这些我都试图摆脱C代码,从RBGA到BGRA,然后所有的索引都很奇怪。我真的不太理解C代码,无法将它应用到PHP代码中。

我还找到了可能有帮助的本站,我现在正在阅读它,如果我想出什么,我会更新的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-07-20 14:52:25

我已经找到你的解决办法了。需要对RLE解码函数进行调整,以支持32位像素格式:即:AARRGGBB

因此,首先,我们需要按照注释中的建议绕过库的颜色深度检查,所以我们将的检查更改为这个

代码语言:javascript
运行
复制
if ($header['pixel_size'] != 24 && $header['pixel_size'] != 32) {
    die('Unsupported TGA color depth'); 
}

然后,我们需要改变一些变量,是颜色的深度相关。它们中的大多数将用于数据格式化,因此无论颜色深度如何,我们都需要它们具有正确的值。这将是:

104 $bytes = $header['pixel_size'] / 8;

117 $size = $header['width'] * $header['height'] * $bytes;

154 $pixels = str_split($data, $bytes);

删除行153**:** $num_bytes = $header['pixel_size']/8;我们不再需要它了。

然后,我们将需要rle_decode()函数来知道像素的格式大小,因此在调用for循环时,我们需要传递这个参数,因此我们还需要更改以下一行:

141**:** 线 $data = rle_decode($data, $size, $bytes);

因此,函数原型转换为:

9线 function rle_decode($data, $datalen, $pixel_size)

现在您可以在这个函数上看到多个3 魔术数字(即第29行上的for ($j = 0; $j<3*$value; $j++) )。所有这些都必须由像素大小参数替换。(3 for 24 bits,4 for 32 bits)

此外,在创建像素时,有时它只编写3字节,而不是4,因此我们也必须对此进行调整。

最后,这给出了以下算法:

代码语言:javascript
运行
复制
function rle_decode($data, $datalen, $pixel_size)
{
    $len = strlen($data);

    $out = '';

    $i = 0;
    $k = 0;
    while ($i<$len)
    {
        dec_bits(ord($data[$i]), $type, $value);
        if ($k >= $datalen)
        {
            break;
        }

        $i++;

        if ($type == 0) //raw
        {
            for ($j=0; $j<$pixel_size*$value; $j++)
            {
                $out .= $data[$j+$i];
                $k++;           
            }
            $i += $value*$pixel_size;
        }
        else //rle
        {
            for ($j=0; $j<$value; $j++)
            {
                $out .= $data[$i] . $data[$i+1] . $data[$i+2];
                if ($pixel_size == 4) $out .= $data[$i+3];
                $k++;               
            }
            $i += $pixel_size;
        }   
    }
    return $out;
}

就是这样,您现在将得到您的TGA图像完美转换。我给您提供了完整的tga.php代码,这样您就可以直接粘贴它并自行测试它:

代码语言:javascript
运行
复制
<?php

// Author: de77
// Licence: MIT
// First-version: 9.02.2010
// Version: 24.08.2010
// http://de77.com

function rle_decode($data, $datalen, $pixel_size)
{
    $len = strlen($data);

    $out = '';

    $i = 0;
    $k = 0;
    while ($i<$len)
    {
        dec_bits(ord($data[$i]), $type, $value);
        if ($k >= $datalen)
        {
            break;
        }

        $i++;

        if ($type == 0) //raw
        {
            for ($j=0; $j<$pixel_size*$value; $j++)
            {
                $out .= $data[$j+$i];
                $k++;           
            }
            $i += $value*$pixel_size;
        }
        else //rle
        {
            for ($j=0; $j<$value; $j++)
            {
                $out .= $data[$i] . $data[$i+1] . $data[$i+2];
                if ($pixel_size == 4) $out .= $data[$i+3];
                $k++;               
            }
            $i += $pixel_size;
        }   
    }
    return $out;
}

function dec_bits($byte, &$type, &$value)
{
    $type = ($byte & 0x80) >> 7;
    $value = 1 + ($byte & 0x7F);
}

function getimagesizetga($filename)
{
    $f = fopen($filename, 'rb');
    $header = fread($f, 18);
    $header = @unpack(  "cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
                        "ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
                        "cpixel_size/cdescriptor", $header);
    fclose($f);

    $types = array(0,1,2,3,9,10,11,32,33);      
    if (!in_array($header['image_type'], $types))
    {
        return array(0, 0, 0, 0, 0);
    }

    if ($header['pixel_size'] > 32)
    {
        return array(0, 0, 0, 0, 0);
    }

    return array($header['width'], $header['height'], 'tga', $header['pixel_size'], $header['image_type']);
}

function imagecreatefromtga($filename)
{
    $f = fopen($filename, 'rb');
    if (!$f)
    {
        return false;
    }
    $header = fread($f, 18);
    $header = unpack(   "cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
                        "ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
                        "cpixel_size/cdescriptor", $header);

    switch ($header['image_type'])
    {
        case 2:     echo "Image is not compressed\n";//no palette, uncompressed
        case 10:    //no palette, rle
                    break;
        default:    die('Unsupported TGA format');                  
    }

    if ($header['pixel_size'] != 24 && $header['pixel_size'] != 32)
    {
        die('Unsupported TGA color depth'); 
    }

    $bytes = $header['pixel_size'] / 8;

    if ($header['image_id_len'] > 0)
    {
        $header['image_id'] = fread($f, $header['image_id_len']);
    }
    else
    {
        $header['image_id'] = '';   
    }

    $im = imagecreatetruecolor($header['width'], $header['height']);

    $size = $header['width'] * $header['height'] * $bytes;

    //-- check whether this is NEW TGA or not
    $pos = ftell($f);
    fseek($f, -26, SEEK_END);   
    $newtga = fread($f, 26);
    if (substr($newtga, 8, 16) != 'TRUEVISION-XFILE')
    {
        $newtga = false;
    }

    fseek($f, 0, SEEK_END);
    $datasize = ftell($f) - $pos; 
    if ($newtga)
    {
        $datasize -= 26;
    }

    fseek($f, $pos, SEEK_SET);

    //-- end of check
    $data = fread($f, $datasize);
    if ($header['image_type'] == 10)
    {
        $data = rle_decode($data, $size, $bytes);                   
    }
    if (bit5($header['descriptor']) == 1)
    {
        $reverse = true;    
    }
    else
    {
        $reverse = false;
    }    


    $i = 0;
    $pixels = str_split($data, $bytes);

    //read pixels 
    if ($reverse)
    {   
        for ($y=0; $y<$header['height']; $y++)
        {       
            for ($x=0; $x<$header['width']; $x++)
            {
                imagesetpixel($im, $x, $y, dwordize($pixels[$i]));
                $i++;
            }
        }
    }
    else
    {
        for ($y=$header['height']-1; $y>=0; $y--)
        {       
            for ($x=0; $x<$header['width']; $x++)
            {
                imagesetpixel($im, $x, $y, dwordize($pixels[$i]));
                $i++;
            }
        }
    }
    fclose($f);         

    return $im;
}

function dwordize($str)
{
    $a = ord($str[0]);
    $b = ord($str[1]);
    $c = ord($str[2]);
    return $c*256*256 + $b*256 + $a;
}

function bit5($x)
{
    return ($x & 32) >> 5;  
}

下面是直接从脚本创建的输出PNG映像:

如果您更喜欢直接下载链接而不是粘贴固定代码,我在这里给您提供完整的php文件:https://www.sendspace.com/file/92uir9

最后,只要更改tga.php文件,一切都会自动工作。

票数 3
EN

Stack Overflow用户

发布于 2022-10-14 13:15:34

基于de77.com代码,但添加了一些内容:

代码语言:javascript
运行
复制
<?php

// References:
// https://stackoverflow.com/questions/24709142/convert-32-bit-tga-to-png
// https://github.com/nothings/stb/blob/master/stb_image.h
// http://tfc.duke.free.fr/coding/tga_specs.pdf
// http://www.paulbourke.net/dataformats/tga/
//
// This class development started with the following code:
// http://de77.com/tga-imagetga-imagecreatefromtga
// Author: de77
// Licence: MIT
// First-version: 9.02.2010
// Version: 24.08.2010
// http://de77.com
//
// C.. oct 2022
// I put the code in a class,
// I added the color-mapped formats,
// I added the 15b & 16b pixel-size formats.
//
// The following is supported:
//
//      Pixel-depths: 8b, 15b, 16b, 24b, 32b
//
//      Image Type      Description
//          0           No Image Data Included
//          1           Uncompressed, Color-mapped Image
//          2           Uncompressed, True-color Image
//          3           Uncompressed, Grayscale Image
//          9           Run-length encoded, Color-mapped Image
//          10          Run-length encoded, True-color Image
//          11          Run-length encoded, Grayscale Image
//
// NOTE: i commented out 16b-alpha code using:    //!
// It does not work, and images come out all transparent.

class uje_tga_class {
    private function rle_decode(&$data, $bytes) {
        $out = '';
        $i = 0;
        $len = strlen($data);
        while ($i<$len) {
            $b = ord($data[$i]);
            $type = ($b & 0x80);
            $count = 1 + ($b & 0x7F);
            $i++;
            // raw or RLE
            if ($type == 0) { //raw
                $size = $bytes*$count;
                $out .= substr($data, $i, $size);
                $i += $size;
            } else { //rle
                $s = substr($data, $i, $bytes);
                $out .= str_repeat($s, $count);
                $i += $bytes;
            }
        }
        return $out;
    }

    private function unmapcolors(&$data, $npixels, $colormapentrybytes, &$palette, $pixelbytes) {
        $out = '';
        for ($i=0, $i2=0; $i<$npixels; $i++, $i2+=$colormapentrybytes) {
            $idx = ord($data[$i2]); // $colormapentrybytes == 1 or 2
            if ($colormapentrybytes == 2) $idx += (ord($data[$i2+1]) << 8);
            $idx *= $pixelbytes;
            $out .= substr($palette, $idx, $pixelbytes);
        }
        return $out;
    }

    public function getimagesizetga($filename)  {
        $f = fopen($filename, 'rb');
        $header = fread($f, 18);
        $header = @unpack("cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
                        "ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
                        "cpixel_size/cdescriptor", $header);
        fclose($f);
        $types = array(0,1,2,3,9,10,11,32,33);      
        if (!in_array($header['image_type'], $types)) return array(0, 0, 0, 0, 0);
        if ($header['pixel_size'] > 32) return array(0, 0, 0, 0, 0);
        return array($header['width'], $header['height'], 'tga', $header['pixel_size'], $header['image_type']);
    }
    
    public function imagecreatefromtga($filename) {
        // open the TGA file for binary reading
        $f = fopen($filename, 'rb');
        if (!$f) return false;
        // read the file
        try {
            // read the TGA header
            $header = fread($f, 18);
            $header = unpack("cimage_id_len/ccolor_map_type/cimage_type/" .
                            "vcolor_map_origin/vcolor_map_len/ccolor_map_entry_size/" .
                            "vx_origin/vy_origin/vwidth/vheight/" .
                            "cpixel_size/cdescriptor", $header);
                            
            // check for supported tga formats
            switch ($header['image_type']) {
                case 1:     // color-mapped uncompressed
                case 2:     // truecolor uncompressed
                case 3:     // grayscale uncompressed       
                case 9:     // color-mapped run-length encoded
                case 10:    // truecolor run-length encoded
                case 11:    // grayscale run-length encoded
                    break;
                default:
                    throw new RuntimeException('Unsupported format: '. $header['image_type']);
            }
            
            $iscolormapped = ($header['image_type'] == 9 || $header['image_type'] == 1);
            $isrle = ($header['image_type'] == 9 || $header['image_type'] == 10 || $header['image_type'] == 11);

             // check for supported pixel sizes. ($header['pixel_size'])
            // But if this is a colormapped image, that "pixel_size" is in fact the width of each entry in the colormap.
            // For a colormapped image, the pixelsize is stored in $header['color_map_entry_size'].
            if ($iscolormapped) {
                if ($header['color_map_type'] == 0) {
                    throw new RuntimeException('Missing colormap');
                }
                $pixelbits = $header['color_map_entry_size'];
                // the entries in the colormap can be 8 or 16 bit wide
                $colormapentrybytes = $header['pixel_size']; // this is really the number of bits..
                if (($colormapentrybytes != 8) && ($colormapentrybytes != 16)) {
                    throw new RuntimeException('Unsupported colormap entry bits: '. $colormapentrybytes);
                }
                $colormapentrybytes /= 8; // ..now it's bytes
            } else {
                $pixelbits = $header['pixel_size'];
            }
            switch ($pixelbits) {
                case 8:                     // grayscale
                    $pixelbytes = 1;
                    break;
                case 15:                    // truecolor    5b blue + 5b green + 5b red + 1b dummy
                case 16:                    // truecolor    5b blue + 5b green + 5b red + 1b alpha
                    $pixelbytes = 2;
                    break;
                case 24:                    // truecolor
                    $pixelbytes = 3;
                    break;
                case 32:                    // truecolor
                    $pixelbytes = 4;
                    break;
                default:
                    throw new RuntimeException('Unsupported pixel bits: '. $pixelbits);
            }

            // skip the image_id
            $header['image_id'] = ($header['image_id_len'] > 0)? fread($f, $header['image_id_len']) : '';

            // check whether this is a TGA v2.0
            $tgav2 = true;
            $pos = ftell($f); // store the current filepointer
            fseek($f, -26, SEEK_END); // (possibly) read the file footer
            $footer = fread($f, 26);
            if (substr($footer, 8, 16) != 'TRUEVISION-XFILE') $tgav2 = false;
            fseek($f, 0, SEEK_END);
            $datasize = ftell($f) - $pos; 
            if ($tgav2) $datasize -= 26; // pixeldata does not include any footer
            fseek($f, $pos, SEEK_SET); // restore filepointer

            // if color-mapped then read the palette.
            // The palette starts at the file-location where the image-data starts for the other formats.
            // So first read the palette, and then correct the final datasize.
            if ($iscolormapped) {
                $palettesize = $header['color_map_len'] * $pixelbytes;
                $pos = ftell($f) + $header['color_map_origin'];
                fseek($f, $pos, SEEK_SET); // set filepointer to palette
                $palette = fread($f, $palettesize);
                $datasize -= $palettesize;
            }

            // Read the image data.
            // If this is a colormapped image then this is not the pixeldata, but the indexes into the colormap.
            $data = fread($f, $datasize);
            
        } catch (Exception $e) {
            //echo 'Exception: ',  $e->getMessage(), "\n";
            return false;
        } finally {
            fclose($f);
        }

        // get the pixeldata
        if ($iscolormapped) {
            $npixels = $header['width'] * $header['height'];
            // colormapped images have the color-indexes encoded (pixeldata must be looked-up after RL decoding)
            if ($isrle) $data = $this->rle_decode($data, $colormapentrybytes);
            $pixeldata = $this->unmapcolors($data, $npixels, $colormapentrybytes, $palette, $pixelbytes);
        } else
            if ($isrle) { // possibly Run Length decode
                $pixeldata = $this->rle_decode($data, $pixelbytes);
            } else // uncompressed
                $pixeldata = $data;
        
        // create the image
        $im = imagecreatetruecolor($header['width'], $header['height']);
        
        // if the image has alpha data, then prepare for it
        imagealphablending($im, false); // no blending. Just copy the pixel
//!     if (!$iscolormapped && ($header['pixel_size'] == 32 || $header['pixel_size'] == 16)) {
        if (!$iscolormapped && ($header['pixel_size'] == 32)) {
            imagesavealpha($im, true); // be sure to save the alpha data
        } else {
            imagesavealpha($im, false);
        }
        
        // read pixel-ordering
        $toptobottom = (($header['descriptor'] & 32) != 0);
        $righttoleft = (($header['descriptor'] & 16) != 0);
        
        // process the pixels
        $i = 0;
        for ($y=0; $y<$header['height']; $y++)  
            for ($x=0; $x<$header['width']; $x++) {
                switch($pixelbytes) {
                    case 1:
                        $r = $g = $b = ord($pixeldata[$i]);
                        $col = imagecolorallocate($im, $r,$g,$b);
                        break;
                    case 2:
                        $r = (ord($pixeldata[$i+1]) & 0x7C) >> 2;
                        $g = ((ord($pixeldata[$i]) & 0xE0) >> 5) + ((ord($pixeldata[$i+1]) & 0x03) << 3);
                        $b = ord($pixeldata[$i]) & 0x1F;
                        $r <<= 3;
                        $g <<= 3;
                        $b <<= 3;
//!                     if ($header['pixel_size'] == 16) {
//!                         $a = ((ord($pixeldata[$i+1]) & 0x80) == 0)? 127 : 0; // 1b alpha means? transparent : opaque
//!                         $col = imagecolorallocatealpha($im, $r,$g,$b,$a);
//!                     } else {
                            $col = imagecolorallocate($im, $r,$g,$b);
//!                     }
                        break;
                    case 3:
                        $r = ord($pixeldata[$i+2]);
                        $g = ord($pixeldata[$i+1]);
                        $b = ord($pixeldata[$i]);
                        $col = imagecolorallocate($im, $r,$g,$b);
                        break;
                    case 4:
                        $r = ord($pixeldata[$i+2]);
                        $g = ord($pixeldata[$i+1]);
                        $b = ord($pixeldata[$i]);
                        $a = 127 - (ord($pixeldata[$i+3]) >> 1); // 128 alpha values with png transulency (where 127 = transparent, 0 = opaque)
                        $col = imagecolorallocatealpha($im, $r,$g,$b,$a);
                        break;
                }
                // set the pixel in the image
                $xp = ($righttoleft)? $header['width'] - 1 - $x : $x;
                $yp = ($toptobottom)? $y : $header['height'] - 1 - $y;
                imagesetpixel($im, $xp, $yp, $col);
                // next pixel in the pixeldata
                $i += $pixelbytes;
            }
        return $im;
    }
    
    public function imagetga($im, $filename) {
        list($width, $height) = array(imagesx($im), imagesy($im));
        $tga = "\0\0\2\0\0\0\0\0\0\0\0\0" . pack('vv', $width, $height) . '  ';
        for ($y=0; $y<$height; $y++)
            for ($x=0; $x<$width; $x++) {
                $rgb = imagecolorat($im, $x, $y);
                $r = ($rgb >> 16) & 0xFF;
                $g = ($rgb >> 8) & 0xFF;
                $b = $rgb & 0xFF;
                $a = ($rgb >> 24) & 0x7F;
                $tga .= chr($b).chr($g).chr($r).chr((127-$a)*2);
            }
        file_put_contents($filename, $tga);
    }

    public function tga2png($tga_filename, $png_filename) {
        $im = $this->imagecreatefromtga($tga_filename);
        if (!$im) return false;
        //header('Content-Disposition: Attachment;filename=image.png');
        //header("Content-type: image/png");
        imagepng($im, $png_filename);
        imagedestroy($im);
        return true;
    }
}

?>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24709142

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档