我正在尝试加载文件并将其转换为图像。文件格式称为Infernal Machine MAT,用于游戏Star Wars Jedi Knight Dark Forces 2中的纹理。该文件有2种颜色深度,8位和16位。它们几乎是Bitmap图像,除了它们缺少各种标题和调色板信息。
关于Infernal Machine MAT的规格可以在Google上输入:“Jedi Knight Unofficial Specs Millennium”和“JK Hub mat2”。
现在,我不是最好的程序员,但我慢慢地设法理解了这种文件格式。我想学习这个,因为我编辑了这个游戏,并为它创建了一些纹理,并认为在我的网站上显示它们会很不错。我想要显示它们的方式不是将它们转换为侧面的PNG,而是直接加载它们(例如,如果有更改,我就不必上传MAT文件并记得上传另一个PNG)。
然而,我似乎遇到了一些障碍。我能做的最好的是逐像素地加载/转换图像,这对于大型MAT文件来说是耗时且超时的。我尝试直接输入数据作为imagecreatefromstring(),但失败了(我猜是因为缺少调色板信息)。有没有办法加快进程,而不是逐点进行?
我的代码如下所示:http://www.edwardleuf.org/Games/JK/IM_MAT_Loader.zip
// Create our palette
$colormap=imagecreate(256,1);
for($i=0; $i<256; $i++)
{
$color=imagecolorallocate($colormap,$cmpR[$i],$cmpG[$i],$cmpB[$i]);
imagesetpixel($colormap,$i,0,$color);
}
// Read whole mat into one string.
$wholemat = "";
if($zipfile!="")
{
$zip = zip_open($zipfile);
if(is_resource($zip))
{
While ($zip_entry = zip_read($zip))
{
$zip_ename = zip_entry_name($zip_entry);
if($matfile!="" && $zip_ename==$matfile)
{
$wholemat = zip_entry_read($zip_entry,zip_entry_filesize($zip_entry));
}
else if($matfile=="" && strtoupper(substr(strrchr($zip_ename,"."),1)) == "MAT")
{
$wholemat = zip_entry_read($zip_entry,zip_entry_filesize($zip_entry));
}
}
}
zip_close($zip);
}
else
{
if($matfile!="")
{
$mat = fopen($matfile,'r');
$wholemat = fread($mat,filesize($matfile));
fclose($mat);
}
}
if($wholemat=="" || substr($wholemat,0,5)!="MAT 2")
{ // If we weren't successful in procuring a proper MAT file
// produce a 2x2 blank
header('Content-type: image/png');
$img = imagecreatetruecolor(2,2);
imagepng($img,'',9);
imagedestroy($img);
return;
}
// Type: 0 = single color, 1 = ?, 2 = full texture
$u = unpack("L",substr($wholemat,8,4));
$matType = $u[1];
// Number of textures or colors
$u = unpack("L",substr($wholemat,12,4));
$matRecordCount = $u[1];
// If single color, it is 0. If full, same as RecordCount
$u = unpack("L",substr($wholemat,16,4));
$matTextureCount = $u[1];
// 8 or 16 bits
$u = unpack("L",substr($wholemat,24,4));
$matBitDepth = $u[1];
$u = unpack("L",substr($wholemat,28,4));
$matBlueBits = $u[1]; // 0, 5, 8
$u = unpack("L",substr($wholemat,32,4));
$matGreenBits = $u[1]; // 0, 6 (16-bit 565), 5 (16-bit 1555), 8
$u = unpack("L",substr($wholemat,36,4));
$matRedBits = $u[1]; // 0, 5, 8
// The shift offset for the location of the R, G and B color values
// in the bitmap data. The color data is extracted by shifting
// the opposite direction of these values.
$u = unpack("L",substr($wholemat,40,4));
$matRedShiftL = $u[1]; // 11 for RGB565, 10 for ARGB1555
$u = unpack("L",substr($wholemat,44,4));
$matGreenShiftL = $u[1]; // 5
$u = unpack("L",substr($wholemat,48,4));
$matBlueShiftL = $u[1]; // 0
// The amount to shift the extracted color values to expand them from
// 5 or 6 bit values to 8 bit values. Unsure if JK actually uses these.
$u = unpack("L",substr($wholemat,52,4));
$matRedShiftR = $u[1]; // 3
$u = unpack("L",substr($wholemat,56,4));
$matGreenShiftR = $u[1]; // 2
$u = unpack("L",substr($wholemat,60,4));
$matBlueShiftR = $u[1]; // 3
$u = unpack("L",substr($wholemat,80,4));
$matTransColor = $u[1];
if($matType==0)
{ // Single Color Mat
if($matBitDepth==8)
{
$img = imagecreate(64*$matRecordCount,64) or die("Cannot Initialize new GD image stream");
imagepalettecopy($img,$colormap);
}
else if($matBitDepth==16)
{
$img = imagecreatetruecolor(64*$matRecordCount,64) or die("Cannot Initialize new GD image stream");
}
for($i=0; $i<$matRecordCount; $i++)
{
$u = unpack("L",substr($wholemat,80+($i*24),4));
if($matBitDepth==8)
{
$carray = imagecolorsforindex($img,$u[1]);
$color = imagecolorclosest($img,$carray[red],$carray[green],$carray[blue]);
}
else if($matBitDepth==16)
{
$color = $u[1];
}
imagefilledrectangle($img,$i*64,0,$i*64+64,64,$color);
}
}
else if($matType==2)
{ // Full Texture
$starttex = intval(76+$matRecordCount*40);
$u = unpack("L",substr($wholemat,$starttex,4));
$matSizeX = $u[1];
$u = unpack("L",substr($wholemat,$starttex+4,4));
$matSizeY = $u[1];
if($matBitDepth==8)
{
$img = imagecreate($matSizeX*$matRecordCount,$matSizeY) or die("Cannot Initialize new GD image stream");
imagepalettecopy($img,$colormap);
}
else if($matBitDepth==16)
{
$img = imagecreatetruecolor($matSizeX*$matRecordCount,$matSizeY) or die("Cannot Initialize new GD image stream");
}
$matTransparency=0;
for($i=0; $i<$matRecordCount; $i++)
{ // Each animation cel can in theory have different sizes.
$u = unpack("L",substr($wholemat,$starttex,4));
$matSizeX = $u[1];
$u = unpack("L",substr($wholemat,$starttex+4,4));
$matSizeY = $u[1];
if($matTransparency==0)
{
$u = unpack("L",substr($wholemat,$starttex+8,4));
$matTransparency = $u[1];
}
$u = unpack("L",substr($wholemat,$starttex+20,4));
$matMipMaps = $u[1];
if($matBitDepth==8)
{
$strimg = substr($wholemat,($starttex+24),($matSizeX*$matSizeY));
}
else if($matBitDepth==16)
{
$strimg = substr($wholemat,($starttex+24),($matSizeX*$matSizeY*2));
}
$j=0;
for($y=0; $y<$matSizeY; $y++)
{
for($x=$matSizeX*$i; $x<$matSizeX+$matSizeX*$i; $x++)
{
if($matBitDepth==8)
{
$carray = imagecolorsforindex($img,ord(substr($strimg,$j,1)));
$color = imagecolorclosest($img,$carray[red],$carray[green],$carray[blue]);
$j=$j+1;
}
else if($matBitDepth==16)
{
if(strlen(substr($strimg,$j,2))==2)
{
$u = unpack("S",substr($strimg,$j,2));
$xr = ($u[1] & 0xf800) >> 11;
$xg = ($u[1] & 0x07e0) >> 5;
$xb = $u[1] & 0x001f;
$br = pow(2,$matRedBits)-1;
$bg = pow(2,$matGreenBits)-1;
$bb = pow(2,$matBlueBits)-1;
if($br>0) $nr = 255/$br * $xr; else $nr = $xr*8;
if($bg>0) $ng = 255/$bg * $xg; else $ng = $xg*4;
if($bb>0) $nb = 255/$bb * $xb; else $nb = $xb*8;
$color = imagecolorallocate($img,$nr,$ng,$nb);
// echo $nr."\t".$ng."\t".$nb."\n";
$j=$j+2;
}
}
imagesetpixel($img,$x,$y,$color);
}
}
// Jump over MipMaps...
if($matMipMaps>1)
{
$j=$j+(($matSizeX/2)*($matSizeY/2));
if($matMipMaps>2)
{
$j=$j+(($matSizeX/4)*($matSizeY/4));
if($matMipMaps>3)
{
$j=$j+(($matSizeX/8)*($matSizeY/8));
}
}
}
$starttex=$starttex+$j+24;
}
}
if($matBitDepth==8)
{
if($matTransparency!=0)
{
/* Not sure about Transparency Information.
According to sources, this should be the index to mask out,
but when looking at 08tgrate.mat it says index 85
which is not the black that should be transparent.
$carray = imagecolorsforindex($img,$matTransColor);
$color = imagecolorclosest($img,$carray[red],$carray[green],$carray[blue]);
Does JK ignore this completely? */
$color = imagecolorclosest($img,0,0,0);
imagecolortransparent($img,$color);
}
}
else if($matBitDepth==16)
{
if($matTransparency!=0)
{
imagecolortransparent($img,$matTransColor);
}
}
if($thumbW!=0 && $thumbH!=0)
{
$newwidth=$thumbW;
$newheight=(imagesy($img)/imagesx($img))*$newwidth;
if($newheight>$thumbH)
{
$newheight=$thumbH;
$newwidth=(imagesx($img)/imagesy($img))*$newheight;
}
$tmp=imagecreatetruecolor($newwidth,$newheight);
imagecopyresampled($tmp,$img,0,0,0,0,$newwidth,$newheight,imagesx($img),imagesy($img));
header('Content-type: image/png');
imagejpeg($tmp,'',50);
imagedestroy($tmp);
}
else
{
header('Content-type: image/png');
imagepng($img,'',9);
}
imagedestroy($img);
imagedestroy($colormap);
unset($wholemat);
这是使用它的网站:http://www.edwardleuf.org/Games/JK/MATs/
/爱德华