mirror of
https://github.com/crskycode/GARbro.git
synced 2026-07-08 01:30:18 +08:00
Initial commit.
This commit is contained in:
363
GameRes/ImageTIFF.cs
Normal file
363
GameRes/ImageTIFF.cs
Normal file
@@ -0,0 +1,363 @@
|
||||
//! \file ImageTIFF.cs
|
||||
//! \date Mon Jul 07 06:39:45 2014
|
||||
//! \brief TIFF image implementation.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes
|
||||
{
|
||||
#if DEBUG
|
||||
// FIXME
|
||||
// .Net TIFF encoder messes up transparency,
|
||||
// therefore this class is disabled in release build
|
||||
[Export(typeof(ImageFormat))]
|
||||
#endif
|
||||
public class TifFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "TIFF"; } }
|
||||
public override string Description { get { return "Tagged Image File Format"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
|
||||
public TifFormat ()
|
||||
{
|
||||
Extensions = new string[] { "tif", "tiff" };
|
||||
Signatures = new uint[] { 0x002a4949, 0x2a004d4d };
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream file, ImageMetaData info)
|
||||
{
|
||||
var decoder = new TiffBitmapDecoder (file,
|
||||
BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
|
||||
var frame = decoder.Frames[0];
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
var encoder = new TiffBitmapEncoder();
|
||||
encoder.Compression = TiffCompressOption.Zip;
|
||||
encoder.Frames.Add (BitmapFrame.Create (image.Bitmap));
|
||||
encoder.Save (file);
|
||||
}
|
||||
|
||||
private delegate uint UInt32Reader();
|
||||
private delegate ushort UInt16Reader();
|
||||
|
||||
enum TIFF
|
||||
{
|
||||
ImageWidth = 0x100,
|
||||
ImageHeight = 0x101,
|
||||
BitsPerSample = 0x102,
|
||||
Compression = 0x103,
|
||||
SamplesPerPixel = 0x115,
|
||||
XResolution = 0x11a,
|
||||
YResolution = 0x11b,
|
||||
XPosition = 0x11e,
|
||||
YPosition = 0x11f,
|
||||
}
|
||||
enum TagType
|
||||
{
|
||||
Byte = 1,
|
||||
Ascii = 2,
|
||||
Short = 3,
|
||||
Long = 4,
|
||||
Rational = 5,
|
||||
SByte = 6,
|
||||
Undefined = 7,
|
||||
SShort = 8,
|
||||
SLong = 9,
|
||||
SRational = 10,
|
||||
Float = 11,
|
||||
Double = 12,
|
||||
LastKnown = Double,
|
||||
}
|
||||
enum MetaParsed
|
||||
{
|
||||
None = 0,
|
||||
Width = 1,
|
||||
Height = 2,
|
||||
BPP = 4,
|
||||
PosX = 8,
|
||||
PosY = 16,
|
||||
Sufficient = Width|Height|BPP,
|
||||
Complete = Sufficient|PosX|PosY,
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
using (var file = new Parser (stream))
|
||||
return file.ReadMetaData();
|
||||
}
|
||||
|
||||
public class Parser : IDisposable
|
||||
{
|
||||
private BinaryReader m_file;
|
||||
private readonly bool m_is_bigendian;
|
||||
private readonly uint m_first_ifd;
|
||||
private readonly uint[] m_type_size = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 };
|
||||
|
||||
private delegate ushort UInt16Reader ();
|
||||
private delegate uint UInt32Reader ();
|
||||
private delegate ulong UInt64Reader ();
|
||||
|
||||
UInt16Reader ReadUInt16;
|
||||
UInt32Reader ReadUInt32;
|
||||
UInt64Reader ReadUInt64;
|
||||
|
||||
public Parser (Stream file)
|
||||
{
|
||||
m_file = new ArcView.Reader (file);
|
||||
uint signature = m_file.ReadUInt32();
|
||||
m_is_bigendian = 0x2a004d4d == signature;
|
||||
if (m_is_bigendian)
|
||||
{
|
||||
ReadUInt16 = () => Binary.BigEndian (m_file.ReadUInt16());
|
||||
ReadUInt32 = () => Binary.BigEndian (m_file.ReadUInt32());
|
||||
ReadUInt64 = () => Binary.BigEndian (m_file.ReadUInt64());
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadUInt16 = () => m_file.ReadUInt16();
|
||||
ReadUInt32 = () => m_file.ReadUInt32();
|
||||
ReadUInt64 = () => m_file.ReadUInt64();
|
||||
}
|
||||
m_first_ifd = ReadUInt32();
|
||||
}
|
||||
|
||||
public long FindLastIFD ()
|
||||
{
|
||||
uint ifd = m_first_ifd;
|
||||
for (;;)
|
||||
{
|
||||
m_file.BaseStream.Position = ifd;
|
||||
uint tag_count = ReadUInt16();
|
||||
ifd += 2 + tag_count*12;
|
||||
uint ifd_next = ReadUInt32();
|
||||
if (0 == ifd_next)
|
||||
break;
|
||||
if (ifd_next == ifd || ifd_next >= m_file.BaseStream.Length)
|
||||
return -1;
|
||||
ifd = ifd_next;
|
||||
}
|
||||
return ifd;
|
||||
}
|
||||
|
||||
public ImageMetaData ReadMetaData ()
|
||||
{
|
||||
MetaParsed parsed = MetaParsed.None;
|
||||
int width = 0, height = 0, bpp = 0, pos_x = 0, pos_y = 0;
|
||||
uint ifd = m_first_ifd;
|
||||
while (ifd != 0 && parsed != MetaParsed.Complete)
|
||||
{
|
||||
m_file.BaseStream.Position = ifd;
|
||||
uint tag_count = ReadUInt16();
|
||||
ifd += 2;
|
||||
for (uint i = 0; i < tag_count && parsed != MetaParsed.Complete; ++i)
|
||||
{
|
||||
ushort tag = ReadUInt16();
|
||||
TagType type = (TagType)ReadUInt16();
|
||||
uint count = ReadUInt32();
|
||||
if (0 != count && 0 != type && type <= TagType.LastKnown)
|
||||
{
|
||||
switch ((TIFF)tag)
|
||||
{
|
||||
case TIFF.ImageWidth:
|
||||
if (1 == count)
|
||||
if (ReadOffsetValue (type, out width))
|
||||
parsed |= MetaParsed.Width;
|
||||
break;
|
||||
case TIFF.ImageHeight:
|
||||
if (1 == count)
|
||||
if (ReadOffsetValue (type, out height))
|
||||
parsed |= MetaParsed.Height;
|
||||
break;
|
||||
case TIFF.XPosition:
|
||||
if (1 == count)
|
||||
if (ReadOffsetValue (type, out pos_x))
|
||||
parsed |= MetaParsed.PosX;
|
||||
break;
|
||||
case TIFF.YPosition:
|
||||
if (1 == count)
|
||||
if (ReadOffsetValue (type, out pos_y))
|
||||
parsed |= MetaParsed.PosY;
|
||||
break;
|
||||
case TIFF.BitsPerSample:
|
||||
if (count * GetTypeSize (type) > 4)
|
||||
{
|
||||
var bpp_offset = ReadUInt32();
|
||||
m_file.BaseStream.Position = bpp_offset;
|
||||
}
|
||||
bpp = 0;
|
||||
for (uint b = 0; b < count; ++b)
|
||||
{
|
||||
int plane = 0;
|
||||
ReadValue (type, out plane);
|
||||
bpp += plane;
|
||||
}
|
||||
parsed |= MetaParsed.BPP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ifd += 12;
|
||||
m_file.BaseStream.Position = ifd;
|
||||
}
|
||||
uint ifd_next = ReadUInt32();
|
||||
if (ifd_next == ifd)
|
||||
break;
|
||||
ifd = ifd_next;
|
||||
}
|
||||
if (MetaParsed.Sufficient == (parsed & MetaParsed.Sufficient))
|
||||
return new ImageMetaData() {
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
OffsetX = pos_x,
|
||||
OffsetY = pos_y,
|
||||
BPP = bpp,
|
||||
};
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
uint GetTypeSize (TagType type)
|
||||
{
|
||||
if ((int)type < m_type_size.Length)
|
||||
return m_type_size[(int)type];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ReadOffsetValue (TagType type, out int value)
|
||||
{
|
||||
if (GetTypeSize (type) > 4)
|
||||
m_file.BaseStream.Position = ReadUInt32();
|
||||
return ReadValue (type, out value);
|
||||
}
|
||||
|
||||
bool ReadValue (TagType type, out int value)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TagType.Undefined:
|
||||
case TagType.SByte:
|
||||
case TagType.Byte:
|
||||
value = m_file.ReadByte();
|
||||
break;
|
||||
default:
|
||||
case TagType.Ascii:
|
||||
value = 0;
|
||||
return false;
|
||||
case TagType.SShort:
|
||||
case TagType.Short:
|
||||
value = ReadUInt16();
|
||||
break;
|
||||
case TagType.SLong:
|
||||
case TagType.Long:
|
||||
value = (int)ReadUInt32();
|
||||
break;
|
||||
case TagType.Rational:
|
||||
return ReadRational (out value);
|
||||
case TagType.SRational:
|
||||
return ReadSRational (out value);
|
||||
case TagType.Float:
|
||||
return ReadFloat (out value);
|
||||
case TagType.Double:
|
||||
return ReadDouble (out value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadRational (out int value)
|
||||
{
|
||||
uint numer = ReadUInt32();
|
||||
uint denom = ReadUInt32();
|
||||
if (1 == denom)
|
||||
value = (int)numer;
|
||||
else if (0 == denom)
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
value = (int)((double)numer / denom);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadSRational (out int value)
|
||||
{
|
||||
int numer = (int)ReadUInt32();
|
||||
int denom = (int)ReadUInt32();
|
||||
if (1 == denom)
|
||||
value = numer;
|
||||
else if (0 == denom)
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
value = (int)((double)numer / denom);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadFloat (out int value)
|
||||
{
|
||||
var convert_buffer = new byte[4];
|
||||
if (4 != m_file.Read (convert_buffer, 0, 4))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
if (m_is_bigendian)
|
||||
Array.Reverse (convert_buffer);
|
||||
value = (int)BitConverter.ToSingle (convert_buffer, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadDouble (out int value)
|
||||
{
|
||||
var convert_buffer = new byte[8];
|
||||
if (8 != m_file.Read (convert_buffer, 0, 8))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
if (m_is_bigendian)
|
||||
Array.Reverse (convert_buffer);
|
||||
long bits = BitConverter.ToInt64 (convert_buffer, 0);
|
||||
value = (int)BitConverter.Int64BitsToDouble (bits);
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose (true);
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
m_file.Dispose();
|
||||
}
|
||||
m_file = null;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user