[Mono-list] TIFF Images
Jonathan Gilbert
2a5gjx302 at sneakemail.com
Sun Aug 27 00:55:58 EDT 2006
At 11:38 AM 21/08/2006 -0700, you wrote:
>Anyone know of a C# TIFF image reader and writer? Any that can read/write
>TIFF Tag information.
Perhaps you will find the attached useful. :-)
Jonathan Gilbert
-------------- next part --------------
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;
namespace ProfileTool
{
public enum TIFFColourSpace
{
RGB,
CMYK,
Lab,
}
public class TIFFImageInfo
{
public TIFFColourSpace ColourSpace;
public int BitsPerComponent;
public Bitmap BitmapData;
}
public class TIFF
{
public static TIFFImageInfo LoadTIFF(string filename)
{
FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
try
{
return LoadTIFF(file);
}
finally
{
file.Close();
}
}
public static TIFFImageInfo LoadTIFF(Stream seekable_stream)
{
return LoadTIFF(seekable_stream, 0);
}
struct TIFFHeader
{
public bool IntelByteOrder;
public int IFDOffset;
}
enum TIFFTag : ushort
{
NewSubfileType = 0x00FE,
SubfileType = 0x00FF,
ImageWidth = 0x0100,
ImageLength = 0x0101,
BitsPerSample = 0x0102,
Compression = 0x0103,
PhotometricInterpretation = 0x0106,
Threshholding = 0x0107,
CellWidth = 0x0108,
CellLength = 0x0109,
FillOrder = 0x010A,
DocumentName = 0x010D,
ImageDescription = 0x010E,
Make = 0x010F,
Model = 0x0110,
StripOffsets = 0x0111,
Orientation = 0x0112,
SamplesPerPixel = 0x0115,
RowsPerStrip = 0x0116,
StripByteCounts = 0x0117,
MinSampleValue = 0x0118,
MaxSampleValue = 0x0119,
XResolution = 0x011A,
YResolution = 0x011B,
PlanarConfiguration = 0x011C,
PageName = 0x011D,
XPosition = 0x011E,
YPosition = 0x011F,
FreeOffsets = 0x0120,
FreeByteCounts = 0x0121,
GrayResponseUnit = 0x0122,
GrayResponseCurve = 0x0123,
T4Options = 0x0124,
T6Options = 0x0125,
ResolutionUnit = 0x0128,
PageNumber = 0x0129,
TransferFunction = 0x012D,
Software = 0x0131,
DateTime = 0x0132,
Artist = 0x013B,
HostComputer = 0x013C,
Predictor = 0x013D,
WhitePoint = 0x013E,
PrimaryChromaticities = 0x013F,
ColorMap = 0x0140,
HalftoneHints = 0x0141,
TileWidth = 0x0142,
TileLength = 0x0143,
TileOffsets = 0x0144,
TileByteCounts = 0x0145,
InkSet = 0x014C,
InkNames = 0x014D,
NumberOfInks = 0x014E,
DotRange = 0x0150,
TargetPrinter = 0x0151,
ExtraSamples = 0x0152,
SampleFormat = 0x0153,
SMinSampleValue = 0x0154,
SMaxSampleValue = 0x0155,
TransferRange = 0x0156,
JPEGProc = 0x0200,
JPEGInterchangeFormat = 0x0201,
JPEGInterchangeFormatLength = 0x0202,
JPEGRestartInterval = 0x0203,
JPEGLosslessPredictors = 0x0205,
JPEGPointTransforms = 0x0206,
JPEGQTables = 0x0207,
JPEGDCTables = 0x0208,
JPEGACTables = 0x0209,
YCbCrCoefficients = 0x0211,
YCbCrSubSampling = 0x0212,
YCbCrPositioning = 0x0213,
ReferenceBlackWhite = 0x0214,
Copyright = 0x8298,
}
enum TIFFType : ushort
{
Byte = 1,
ASCII = 2,
Short = 3,
Long = 4,
Rational = 5,
SignedByte = 6,
Undefined = 7,
SignedShort = 8,
SignedLong = 9,
SignedRational = 10,
Float = 11,
Double = 12,
}
enum TIFFPhotometricInterpretation : uint
{
Bilevel_WhiteIsZero = 0,
Bilevel_BlackIsZero = 1,
RGB = 2,
RGB_Palette = 3,
TransparencyMask = 4,
CMYK = 5,
YCbCr = 6,
CIELab = 8,
}
enum TIFFCompression : uint
{
Uncompressed = 1,
CCITT_1D = 2,
Group3Fax = 3,
Group4Fax = 4,
LZW = 5,
JPEG = 6,
PackBits = 32773,
}
static int bytes_for_type(TIFFType type)
{
switch (type)
{
case TIFFType.Byte:
case TIFFType.ASCII:
case TIFFType.SignedByte:
case TIFFType.Undefined: return 1;
case TIFFType.Short:
case TIFFType.SignedShort: return 2;
case TIFFType.Long:
case TIFFType.SignedLong:
case TIFFType.Float: return 4;
case TIFFType.Rational:
case TIFFType.SignedRational:
case TIFFType.Double: return 8;
default: return 0;
}
}
struct Rational
{
public uint Numerator, Denominator;
}
struct SignedRational
{
public int Numerator, Denominator;
}
class TIFFIFDEntry
{
public TIFFTag Tag;
public TIFFType Type;
public int Count;
public uint FirstValueOffset;
public void ReadFrom(BinaryReader reader, int base_offset)
{
Tag = (TIFFTag)reader.ReadUInt16();
Type = (TIFFType)reader.ReadUInt16();
Count = reader.ReadInt32();
int num_bytes = Count * bytes_for_type(Type);
FirstValueOffset = reader.ReadUInt32();
if (num_bytes <= 4)
FirstValueOffset = (uint)(reader.BaseStream.Position - base_offset - 4);
}
public object GetData(BinaryReader reader, int base_offset)
{
reader.BaseStream.Seek(base_offset + FirstValueOffset, SeekOrigin.Begin);
switch (Type)
{
case TIFFType.Byte:
case TIFFType.Undefined:
{
byte[] ret = reader.ReadBytes(Count);
return ret;
}
case TIFFType.ASCII:
{
byte[] bytes = reader.ReadBytes(Count);
return Encoding.ASCII.GetString(bytes);
}
case TIFFType.Short:
{
ushort[] ret = new ushort[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadUInt16();
return ret;
}
case TIFFType.Long:
{
uint[] ret = new uint[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadUInt32();
return ret;
}
case TIFFType.Rational:
{
Rational[] ret = new Rational[Count];
for (int i=0; i < Count; i++)
{
ret[i].Numerator = reader.ReadUInt32();
ret[i].Denominator = reader.ReadUInt32();
}
return ret;
}
case TIFFType.SignedByte:
{
sbyte[] ret = new sbyte[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadSByte();
return ret;
}
case TIFFType.SignedShort:
{
short[] ret = new short[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadInt16();
return ret;
}
case TIFFType.SignedLong:
{
int[] ret = new int[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadInt32();
return ret;
}
case TIFFType.SignedRational:
{
SignedRational[] ret = new SignedRational[Count];
for (int i=0; i < Count; i++)
{
ret[i].Numerator = reader.ReadInt32();
ret[i].Denominator = reader.ReadInt32();
}
return ret;
}
case TIFFType.Float:
{
float[] ret = new float[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadSingle();
return ret;
}
case TIFFType.Double:
{
double[] ret = new double[Count];
for (int i=0; i < Count; i++)
ret[i] = reader.ReadDouble();
return ret;
}
default:
return null;
}
}
public uint GetGenericScalar(BinaryReader reader, int base_offset)
{
object data = GetData(reader, base_offset);
if (data is int[])
return (uint)((int[])data)[0];
if (data is short[])
return (uint)((short[])data)[0];
if (data is byte[])
return ((byte[])data)[0];
if (data is uint[])
return ((uint[])data)[0];
if (data is ushort[])
return ((ushort[])data)[0];
if (data is sbyte[])
return (uint)((sbyte[])data)[0];
if (data is Rational[])
{
Rational r = ((Rational[])data)[0];
return (uint)(r.Numerator / r.Denominator);
}
if (data is SignedRational[])
{
SignedRational r = ((SignedRational[])data)[0];
return (uint)(r.Numerator / r.Denominator);
}
if (data is float[])
return (uint)((float[])data)[0];
if (data is double[])
return (uint)((double[])data)[0];
if (data is string)
try
{
return uint.Parse(data.ToString());
}
catch (FormatException)
{
return 0;
}
throw new OutOfMemoryException(_("The data could not be interpreted."));
}
public uint[] GetGenericArray(BinaryReader reader, int base_offset)
{
object data = GetData(reader, base_offset);
if (data is Array)
{
Array data_array = (Array)data;
uint[] ret = new uint[data_array.Length];
for (int i=0; i < ret.Length; i++)
ret[i] = Convert.ToUInt32(data_array.GetValue(i));
return ret;
}
else
throw new OutOfMemoryException(_("The data could not be interpreted."));
}
}
class TIFFIFD
{
public TIFFIFDEntry[] Entry;
public uint NextIFDOffset;
public void ReadFrom(BinaryReader reader, int base_offset)
{
int count = reader.ReadUInt16();
Entry = new TIFFIFDEntry[count];
for (int i=0; i < count; i++)
{
Entry[i] = new TIFFIFDEntry();
Entry[i].ReadFrom(reader, base_offset);
}
NextIFDOffset = reader.ReadUInt32();
}
public bool ContainsTag(TIFFTag tag)
{
if (FindTag(tag) != null)
return true;
else
return false;
}
public TIFFIFDEntry FindTag(TIFFTag tag)
{
for (int i=0; i < Entry.Length; i++)
if (Entry[i].Tag == tag)
return Entry[i];
return null;
}
public object GetTagData(TIFFTag tag, BinaryReader reader, int base_offset)
{
TIFFIFDEntry entry = FindTag(tag);
if (entry == null)
return null;
else
return entry.GetData(reader, base_offset);
}
}
class BitReader
{
public readonly Stream BaseStream;
public BitReader(Stream to_wrap)
{
BaseStream = to_wrap;
}
int bits_left = 0, bits;
public bool ReadBit()
{
if (bits_left == 0)
{
bits = BaseStream.ReadByte();
bits_left = 8;
}
bits <<= 1;
bits_left--;
return (0 != (bits & 0x100));
}
public void SyncToNextByte()
{
while (bits_left > 0)
ReadBit();
}
}
class BitWriter
{
public readonly Stream BaseStream;
public BitWriter(Stream to_wrap)
{
BaseStream = to_wrap;
}
int bits_left = 8, bits;
public void WriteBit(bool bit)
{
bits = (bits << 1) | (bit ? 1 : 0);
bits_left--;
if (bits_left == 0)
{
BaseStream.WriteByte((byte)bits);
bits_left = 8;
bits = 0;
}
}
public void SyncToNextByte()
{
while (bits_left != 8)
WriteBit(false);
}
public void Seek(int position)
{
if (bits_left < 8)
BaseStream.WriteByte((byte)bits);
bits_left = 8;
bits = 0;
BaseStream.Seek(position, SeekOrigin.Begin);
}
}
class TIFFHuffman
{
enum CodeType
{
White,
Black,
Both,
}
struct Code
{
public int Value, Length;
public CodeType Type;
public Code(int value, int length, CodeType type)
{
Value = value;
Length = length;
Type = type;
}
}
static Code[][] codes_by_length = new Code[][]
{
new Code[0], // length 0 does not exist
new Code[0], // length 1 does not exist
new Code[] // length 2
{
new Code(3, 2, CodeType.Black), // 11
new Code(1, 3, CodeType.Black), // 10
},
new Code[] // length 3
{
new Code(2, 1, CodeType.Black), // 010
new Code(6, 4, CodeType.Black), // 011
},
new Code[] // length 4
{
new Code(14, 2, CodeType.White), // 0111
new Code(1, 3, CodeType.White), // 1000
new Code(13, 4, CodeType.White), // 1011
new Code(3, 5, CodeType.White), // 1100
new Code(7, 6, CodeType.White), // 1110
new Code(15, 7, CodeType.White), // 1111
new Code(12, 5, CodeType.Black), // 0011
new Code(4, 6, CodeType.Black), // 0010
},
new Code[] // length 5
{
new Code(25, 8, CodeType.White), // 10011
new Code(5, 9, CodeType.White), // 10100
new Code(28, 10, CodeType.White), // 00111
new Code(2, 11, CodeType.White), // 01000
new Code(27, 64, CodeType.White), // 11011
new Code(9, 128, CodeType.White), // 10010
new Code(24, 7, CodeType.Black), // 00011
},
new Code[] // length 6
{
new Code(56, 1, CodeType.White), // 000111
new Code(4, 12, CodeType.White), // 001000
new Code(48, 13, CodeType.White), // 000011
new Code(11, 14, CodeType.White), // 110100
new Code(43, 15, CodeType.White), // 110101
new Code(21, 16, CodeType.White), // 101010
new Code(53, 17, CodeType.White), // 101011
new Code(58, 192, CodeType.White), // 010111
new Code(6, 1664, CodeType.White), // 011000
new Code(40, 8, CodeType.Black), // 000101
new Code(8, 9, CodeType.Black), // 000100
},
new Code[] // length 7
{
new Code(114, 18, CodeType.White), // 0100111
new Code(24, 19, CodeType.White), // 0001100
new Code(8, 20, CodeType.White), // 0001000
new Code(116, 21, CodeType.White), // 0010111
new Code(96, 22, CodeType.White), // 0000011
new Code(16, 23, CodeType.White), // 0000100
new Code(10, 24, CodeType.White), // 0101000
new Code(106, 25, CodeType.White), // 0101011
new Code(100, 26, CodeType.White), // 0010011
new Code(18, 27, CodeType.White), // 0100100
new Code(12, 28, CodeType.White), // 0011000
new Code(118, 256, CodeType.White), // 0110111
new Code(16, 10, CodeType.Black), // 0000100
new Code(80, 11, CodeType.Black), // 0000101
new Code(112, 12, CodeType.Black), // 0000111
},
new Code[] // length 8
{
new Code(172, 0, CodeType.White), // 00110101
new Code(64, 29, CodeType.White), // 00000010
new Code(192, 30, CodeType.White), // 00000011
new Code(88, 31, CodeType.White), // 00011010
new Code(216, 32, CodeType.White), // 00011011
new Code(72, 33, CodeType.White), // 00010010
new Code(200, 34, CodeType.White), // 00010011
new Code(40, 35, CodeType.White), // 00010100
new Code(168, 36, CodeType.White), // 00010101
new Code(104, 37, CodeType.White), // 00010110
new Code(232, 38, CodeType.White), // 00010111
new Code(20, 39, CodeType.White), // 00101000
new Code(148, 40, CodeType.White), // 00101001
new Code(84, 41, CodeType.White), // 00101010
new Code(212, 42, CodeType.White), // 00101011
new Code(52, 43, CodeType.White), // 00101100
new Code(180, 44, CodeType.White), // 00101101
new Code(32, 45, CodeType.White), // 00000100
new Code(160, 46, CodeType.White), // 00000101
new Code(80, 47, CodeType.White), // 00001010
new Code(208, 48, CodeType.White), // 00001011
new Code(74, 49, CodeType.White), // 01010010
new Code(202, 50, CodeType.White), // 01010011
new Code(42, 51, CodeType.White), // 01010100
new Code(170, 52, CodeType.White), // 01010101
new Code(36, 53, CodeType.White), // 00100100
new Code(164, 54, CodeType.White), // 00100101
new Code(26, 55, CodeType.White), // 01011000
new Code(154, 56, CodeType.White), // 01011001
new Code(90, 57, CodeType.White), // 01011010
new Code(218, 58, CodeType.White), // 01011011
new Code(82, 59, CodeType.White), // 01001010
new Code(210, 60, CodeType.White), // 01001011
new Code(76, 61, CodeType.White), // 00110010
new Code(204, 62, CodeType.White), // 00110011
new Code(44, 63, CodeType.White), // 00110100
new Code(108, 320, CodeType.White), // 00110110
new Code(236, 384, CodeType.White), // 00110111
new Code(38, 448, CodeType.White), // 01100100
new Code(166, 512, CodeType.White), // 01100101
new Code(22, 576, CodeType.White), // 01101000
new Code(230, 640, CodeType.White), // 01100111
new Code(32, 13, CodeType.Black), // 00000100
new Code(224, 14, CodeType.Black), // 00000111
},
new Code[] // length 9
{
new Code(102, 704, CodeType.White), // 011001100
new Code(358, 768, CodeType.White), // 011001101
new Code(150, 832, CodeType.White), // 011010010
new Code(406, 896, CodeType.White), // 011010011
new Code(86, 960, CodeType.White), // 011010100
new Code(342, 1024, CodeType.White), // 011010101
new Code(214, 1088, CodeType.White), // 011010110
new Code(470, 1152, CodeType.White), // 011010111
new Code(54, 1216, CodeType.White), // 011011000
new Code(310, 1280, CodeType.White), // 011011001
new Code(182, 1344, CodeType.White), // 011011010
new Code(438, 1408, CodeType.White), // 011011011
new Code(50, 1472, CodeType.White), // 010011000
new Code(306, 1536, CodeType.White), // 010011001
new Code(178, 1600, CodeType.White), // 010011010
new Code(434, 1728, CodeType.White), // 010011011
new Code(48, 15, CodeType.Black), // 000011000
},
new Code[] // length 10
{
new Code(944, 0, CodeType.Black), // 0000110111
new Code(928, 16, CodeType.Black), // 0000010111
new Code(96, 17, CodeType.Black), // 0000011000
new Code(64, 18, CodeType.Black), // 0000001000
new Code(960, 64, CodeType.Black), // 0000001111
},
new Code[] // length 11
{
new Code(1840, 19, CodeType.Black), // 00001100111
new Code(176, 20, CodeType.Black), // 00001101000
new Code(432, 21, CodeType.Black), // 00001101100
new Code(1888, 22, CodeType.Black), // 00000110111
new Code(160, 23, CodeType.Black), // 00000101000
new Code(1856, 24, CodeType.Black), // 00000010111
new Code(192, 25, CodeType.Black), // 00000011000
new Code(0, -1, CodeType.Black), // 00000000000
new Code(128, 1792, CodeType.Both), // 00000001000
new Code(384, 1856, CodeType.Both), // 00000001100
new Code(1408, 1920, CodeType.Both), // 00000001101
},
new Code[] // length 12
{
new Code(2048, -1, CodeType.White), // 000000000001
new Code(1328, 26, CodeType.Black), // 000011001010
new Code(3376, 27, CodeType.Black), // 000011001011
new Code(816, 28, CodeType.Black), // 000011001100
new Code(2864, 29, CodeType.Black), // 000011001101
new Code(352, 30, CodeType.Black), // 000001101000
new Code(2400, 31, CodeType.Black), // 000001101001
new Code(1376, 32, CodeType.Black), // 000001101010
new Code(3424, 33, CodeType.Black), // 000001101011
new Code(1200, 34, CodeType.Black), // 000011010010
new Code(3248, 35, CodeType.Black), // 000011010011
new Code(688, 36, CodeType.Black), // 000011010100
new Code(2736, 37, CodeType.Black), // 000011010101
new Code(1712, 38, CodeType.Black), // 000011010110
new Code(3760, 39, CodeType.Black), // 000011010111
new Code(864, 40, CodeType.Black), // 000001101100
new Code(2912, 41, CodeType.Black), // 000001101101
new Code(1456, 42, CodeType.Black), // 000011011010
new Code(3504, 43, CodeType.Black), // 000011011011
new Code(672, 44, CodeType.Black), // 000001010100
new Code(2720, 45, CodeType.Black), // 000001010101
new Code(1696, 46, CodeType.Black), // 000001010110
new Code(3744, 47, CodeType.Black), // 000001010111
new Code(608, 48, CodeType.Black), // 000001100100
new Code(2656, 49, CodeType.Black), // 000001100101
new Code(1184, 50, CodeType.Black), // 000001010010
new Code(3232, 51, CodeType.Black), // 000001010011
new Code(576, 52, CodeType.Black), // 000000100100
new Code(3776, 53, CodeType.Black), // 000000110111
new Code(448, 54, CodeType.Black), // 000000111000
new Code(3648, 55, CodeType.Black), // 000000100111
new Code(320, 56, CodeType.Black), // 000000101000
new Code(416, 57, CodeType.Black), // 000001011000
new Code(2464, 58, CodeType.Black), // 000001011001
new Code(3392, 59, CodeType.Black), // 000000101011
new Code(832, 60, CodeType.Black), // 000000101100
new Code(1440, 61, CodeType.Black), // 000001011010
new Code(1632, 62, CodeType.Black), // 000001100110
new Code(3680, 63, CodeType.Black), // 000001100111
new Code(304, 128, CodeType.Black), // 000011001000
new Code(2352, 192, CodeType.Black), // 000011001001
new Code(3488, 256, CodeType.Black), // 000001011011
new Code(3264, 320, CodeType.Black), // 000000110011
new Code(704, 384, CodeType.Black), // 000000110100
new Code(2752, 448, CodeType.Black), // 000000110101
new Code(1152, 1984, CodeType.Both), // 000000010010
new Code(3200, 2048, CodeType.Both), // 000000010011
new Code(640, 2112, CodeType.Both), // 000000010100
new Code(2688, 2176, CodeType.Both), // 000000010101
new Code(1664, 2240, CodeType.Both), // 000000010110
new Code(3712, 2304, CodeType.Both), // 000000010111
new Code(896, 2368, CodeType.Both), // 000000011100
new Code(2944, 2432, CodeType.Both), // 000000011101
new Code(1920, 2496, CodeType.Both), // 000000011110
new Code(3968, 2560, CodeType.Both), // 000000011111
},
new Code[] // length 13
{
new Code(1728, 512, CodeType.Black), // 0000001101100
new Code(5824, 576, CodeType.Black), // 0000001101101
new Code(2624, 640, CodeType.Black), // 0000001001010
new Code(6720, 704, CodeType.Black), // 0000001001011
new Code(1600, 768, CodeType.Black), // 0000001001100
new Code(5696, 832, CodeType.Black), // 0000001001101
new Code(2496, 896, CodeType.Black), // 0000001110010
new Code(6592, 960, CodeType.Black), // 0000001110011
new Code(1472, 1024, CodeType.Black), // 0000001110100
new Code(5568, 1088, CodeType.Black), // 0000001110101
new Code(3520, 1152, CodeType.Black), // 0000001110110
new Code(7616, 1216, CodeType.Black), // 0000001110111
new Code(2368, 1280, CodeType.Black), // 0000001010010
new Code(6464, 1344, CodeType.Black), // 0000001010011
new Code(1344, 1408, CodeType.Black), // 0000001010100
new Code(5440, 1472, CodeType.Black), // 0000001010101
new Code(2880, 1536, CodeType.Black), // 0000001011010
new Code(6976, 1600, CodeType.Black), // 0000001011011
new Code(1216, 1664, CodeType.Black), // 0000001100100
new Code(5312, 1728, CodeType.Black), // 0000001100101
}
};
class Tree
{
public Tree Bit0, Bit1;
public int Length;
public int GetLength(BitReader stream)
{
if ((Bit0 == null) && (Bit1 == null))
return Length;
bool bit = stream.ReadBit();
Tree child = (bit == false) ? Bit0 : Bit1;
if (child == null)
throw new OutOfMemoryException(_("Invalid Huffman code"));
return child.GetLength(stream);
}
public void Insert(int bits, int num_bits_remaining, int length)
{
if (num_bits_remaining == 0)
{
if ((Bit0 != null) || (Bit1 != null))
throw new OutOfMemoryException(_("Huffman code table corrupt"));
Length = length;
return;
}
bool bit = (0 != (bits & 1));
bits >>= 1;
num_bits_remaining--;
if (bit == false)
{
if (Bit0 == null)
Bit0 = new Tree();
Bit0.Insert(bits, num_bits_remaining, length);
}
else
{
if (Bit1 == null)
Bit1 = new Tree();
Bit1.Insert(bits, num_bits_remaining, length);
}
}
}
static Tree white_tree, black_tree;
static void build_trees()
{
white_tree = new Tree();
black_tree = new Tree();
for (int length=0; length < codes_by_length.Length; length++)
{
Code[] codes = codes_by_length[length];
for (int i=0; i < codes.Length; i++)
{
Code code = codes[i];
if ((code.Type == CodeType.White) || (code.Type == CodeType.Both))
white_tree.Insert(code.Value, length, code.Length);
if ((code.Type == CodeType.Black) || (code.Type == CodeType.Both))
black_tree.Insert(code.Value, length, code.Length);
}
}
}
static TIFFHuffman()
{
build_trees();
}
public static byte[] Decode(byte[] input, int width, int rows, int stride)
{
byte[] output = new byte[rows * stride];
using (MemoryStream input_stream = new MemoryStream(input),
output_stream = new MemoryStream(output))
{
BitReader reader = new BitReader(input_stream);
BitWriter writer = new BitWriter(output_stream);
for (int y=0; y < rows; y++)
{
int o = y * stride;
int code = 0;
int x=0;
while (x < width)
{
code = white_tree.GetLength(reader);
if (code < 0) // end-of-line
break;
for (int i=0; i < code; i++)
{
if (x < width)
writer.WriteBit(false);
x++;
}
if (x >= width)
{
if (code >= 64)
{
code = white_tree.GetLength(reader);
if (code != 0)
throw new OutOfMemoryException(_("Invalid Huffman data"));
}
break;
}
if (code >= 64)
continue;
do
{
code = black_tree.GetLength(reader);
if (code < 0) // end-of-line
break;
for (int i=0; i < code; i++)
{
if (x < width)
writer.WriteBit(true);
x++;
}
}
while (code >= 64);
}
reader.SyncToNextByte();
writer.SyncToNextByte();
}
}
return output;
}
}
class TIFFPackBits
{
public static byte[] Decode(byte[] input, int width, int rows, int stride)
{
byte[] output = new byte[rows * stride];
int i = 0;
for (int y=0; y < rows; y++)
{
int o = y * stride;
int run = 0;
int run_value = 0;
int x=0;
while (x < stride)
{
if (i >= input.Length)
return output;
sbyte n = unchecked((sbyte)input[i++]);
if (n >= 0)
{
run = 1 + n;
run_value = -1;
}
else if (n > -128)
{
run = 1 - n;
run_value = input[i++];
}
else
continue;
if (run_value < 0)
{
if (run + x > stride)
Array.Copy(input, i, output, o, stride - x);
else
Array.Copy(input, i, output, o, run);
i += run;
}
else
{
if (run + x > stride)
run = stride - x;
for (int q=0; q < run; q++)
output[o + q] = unchecked((byte)run_value);
}
x += run;
o += run;
}
}
return output;
}
}
public static unsafe TIFFImageInfo LoadTIFF(Stream seekable_stream, int offset)
{
seekable_stream.Seek(offset, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(seekable_stream, Encoding.ASCII);
TIFFHeader header = new TIFFHeader();
ushort byte_order = reader.ReadUInt16();
if (byte_order == 0x4949)
header.IntelByteOrder = true;
else
header.IntelByteOrder = false;
if (!header.IntelByteOrder)
{
seekable_stream.Seek(offset + 2, SeekOrigin.Begin);
reader = new MSBBinaryReader(seekable_stream, Encoding.ASCII);
}
ushort meaning_of_life = reader.ReadUInt16();
if (meaning_of_life != 42)
throw new OutOfMemoryException(_("Invalid file format (the meaning of life is not 42)"));
header.IFDOffset = reader.ReadInt32();
int ifd_position = offset + header.IFDOffset;
reader.BaseStream.Seek(ifd_position, SeekOrigin.Begin);
TIFFIFD image_file_directory = new TIFFIFD();
image_file_directory.ReadFrom(reader, offset);
if ((!image_file_directory.ContainsTag(TIFFTag.ImageWidth))
|| (!image_file_directory.ContainsTag(TIFFTag.ImageLength)))
throw new OutOfMemoryException(_("Invalid file format (image dimensions are missing)"));
if (!image_file_directory.ContainsTag(TIFFTag.PhotometricInterpretation))
throw new OutOfMemoryException(_("Invalid file format (there is no photometric interpretation)"));
if ((!image_file_directory.ContainsTag(TIFFTag.StripOffsets))
|| (!image_file_directory.ContainsTag(TIFFTag.StripByteCounts)))
throw new OutOfMemoryException(_("Invalid file format (strip information is missing)"));
int width = (int)
image_file_directory.FindTag(TIFFTag.ImageWidth).GetGenericScalar(reader, offset);
int height = (int)
image_file_directory.FindTag(TIFFTag.ImageLength).GetGenericScalar(reader, offset);
int rows_per_strip, samples_per_pixel, extra_samples, fill_order, orientation;
uint[] bits_per_sample;
if (image_file_directory.ContainsTag(TIFFTag.RowsPerStrip))
rows_per_strip = (int)
image_file_directory.FindTag(TIFFTag.RowsPerStrip).GetGenericScalar(reader, offset);
else
rows_per_strip = int.MaxValue;
if (image_file_directory.ContainsTag(TIFFTag.BitsPerSample))
bits_per_sample =
image_file_directory.FindTag(TIFFTag.BitsPerSample).GetGenericArray(reader, offset);
else
{
bits_per_sample = new uint[1];
bits_per_sample[0] = 1;
}
if (image_file_directory.ContainsTag(TIFFTag.SamplesPerPixel))
samples_per_pixel = (int)
image_file_directory.FindTag(TIFFTag.SamplesPerPixel).GetGenericScalar(reader, offset);
else
samples_per_pixel = 1;
if (image_file_directory.ContainsTag(TIFFTag.ExtraSamples))
extra_samples = (int)
image_file_directory.FindTag(TIFFTag.ExtraSamples).GetGenericScalar(reader, offset);
else
extra_samples = 0;
if (image_file_directory.ContainsTag(TIFFTag.FillOrder))
fill_order = (int)
image_file_directory.FindTag(TIFFTag.FillOrder).GetGenericScalar(reader, offset);
else
fill_order = 1;
if (image_file_directory.ContainsTag(TIFFTag.Orientation))
orientation = (int)
image_file_directory.FindTag(TIFFTag.Orientation).GetGenericScalar(reader, offset);
else
orientation = 1;
uint[] strip_offsets =
image_file_directory.FindTag(TIFFTag.StripOffsets).GetGenericArray(reader, offset);
uint[] strip_byte_counts =
image_file_directory.FindTag(TIFFTag.StripByteCounts).GetGenericArray(reader, offset);
TIFFPhotometricInterpretation photometric_interpretation = (TIFFPhotometricInterpretation)
image_file_directory.FindTag(TIFFTag.PhotometricInterpretation).GetGenericScalar(reader, offset);
TIFFCompression compression = (TIFFCompression)
image_file_directory.FindTag(TIFFTag.Compression).GetGenericScalar(reader, offset);
uint[] colour_map = null;
if (image_file_directory.ContainsTag(TIFFTag.ColorMap))
colour_map =
image_file_directory.FindTag(TIFFTag.ColorMap).GetGenericArray(reader, offset);
int num_strips = (height + rows_per_strip - 1) / rows_per_strip;
if (fill_order != 1)
throw new OutOfMemoryException(_("Unsupported fill order"));
if (orientation != 1)
throw new OutOfMemoryException(_("Unsupported orientation"));
if ((strip_offsets.Length != num_strips) || (strip_byte_counts.Length != num_strips))
throw new OutOfMemoryException(_("Invalid file format (strip data is invalid)"));
if (bits_per_sample.Length < samples_per_pixel)
{
uint[] new_bits_per_sample = new uint[samples_per_pixel];
Array.Copy(bits_per_sample, 0, new_bits_per_sample, 0, bits_per_sample.Length);
for (int i=bits_per_sample.Length; i < new_bits_per_sample.Length; i++)
new_bits_per_sample[i] = bits_per_sample[0];
bits_per_sample = new_bits_per_sample;
}
if (bits_per_sample.Length != samples_per_pixel)
throw new OutOfMemoryException(_("Invalid file format (incorrect number of samples described in BitsPerSample)"));
if (((photometric_interpretation == TIFFPhotometricInterpretation.Bilevel_BlackIsZero)
|| (photometric_interpretation == TIFFPhotometricInterpretation.Bilevel_WhiteIsZero))
&& (samples_per_pixel - extra_samples != 1))
throw new OutOfMemoryException(_("Invalid file format (bilevel image has more than one sample per pixel)"));
if (photometric_interpretation == TIFFPhotometricInterpretation.RGB_Palette)
{
if (samples_per_pixel != 1)
throw new OutOfMemoryException(_("Invalid file format (palettized image has more than one sample per pixel)"));
int expected_colour_map_length = (1 << (int)bits_per_sample[0]) * 3;
if (colour_map.Length != expected_colour_map_length)
throw new OutOfMemoryException(_("Invalid file format (palette does not contain the correct number of entries)"));
}
if (((photometric_interpretation == TIFFPhotometricInterpretation.RGB)
|| (photometric_interpretation == TIFFPhotometricInterpretation.YCbCr))
&& (samples_per_pixel - extra_samples != 3))
throw new OutOfMemoryException(_("Invalid file format (image does not have 3 components)"));
if ((photometric_interpretation == TIFFPhotometricInterpretation.CMYK)
&& (samples_per_pixel - extra_samples != 4))
throw new OutOfMemoryException(_("Invalid file format (CMYK image does not have 4 components)"));
int strip_index = 0;
Bitmap bmp;
bool sixteen_bits = false;
for (int i=0; i < samples_per_pixel; i++)
if (bits_per_sample[i] > 8)
{
sixteen_bits = true;
break;
}
int pixel_bytes = (sixteen_bits ? 8 : 4);
if (sixteen_bits)
bmp = new Bitmap(width, height, PixelFormat.Format64bppArgb);
else
if (photometric_interpretation == TIFFPhotometricInterpretation.CMYK)
bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
else
bmp = new Bitmap(width, height, PixelFormat.Format32bppRgb);
BitmapData bmp_data = null;
int pixel_bits = 0;
for (int i=0; i < samples_per_pixel; i++)
pixel_bits += (int)bits_per_sample[i];
int stride_bits = (int)(width * pixel_bits);
int stride_bytes = (stride_bits + 7) >> 3;
try
{
bmp_data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat);
if (bmp_data == null)
throw new OutOfMemoryException(_("Unable to lock bitmap data"));
byte *bmp_data_ptr = (byte *)bmp_data.Scan0.ToPointer();
int bmp_stride = bmp_data.Stride;
int bmp_fixup = bmp_stride - bmp_data.Width * (sixteen_bits ? 8 : 4);
for (long y=0; y < height; y += rows_per_strip, strip_index++)
{
int strip_position = (int)(offset + strip_offsets[strip_index]);
int rows_this_strip = rows_per_strip;
long strip_end = y + rows_per_strip;
if (strip_end > height)
{
strip_end = height;
rows_this_strip = (int)(strip_end - y);
}
int bmp_data_offset = (int)(y * bmp_stride);
reader.BaseStream.Seek(strip_position, SeekOrigin.Begin);
byte[] bytes = reader.ReadBytes((int)strip_byte_counts[strip_index]);
switch (compression)
{
case TIFFCompression.Uncompressed:
break;
case TIFFCompression.CCITT_1D:
bytes = TIFFHuffman.Decode(bytes, width, rows_this_strip, stride_bytes);
break;
case TIFFCompression.PackBits:
bytes = TIFFPackBits.Decode(bytes, width, rows_this_strip, stride_bytes);
break;
default:
throw new OutOfMemoryException(_("Unsupported compression method"));
}
switch (photometric_interpretation)
{
case TIFFPhotometricInterpretation.Bilevel_BlackIsZero:
case TIFFPhotometricInterpretation.Bilevel_WhiteIsZero:
case TIFFPhotometricInterpretation.TransparencyMask:
{
using (MemoryStream stream = new MemoryStream(bytes))
{
BitReader bits = new BitReader(stream);
for (long yy=y; yy < strip_end; yy++)
{
for (int xx=0; xx < width; xx++)
{
byte bit = 0;
if ((bits_per_sample[0] == 8) && (extra_samples == 0))
bit = (byte)(0xFF ^ stream.ReadByte());
else
unchecked
{
for (int i=0; i < bits_per_sample[0]; i++)
bit = (byte)((bit << 1) | (bits.ReadBit() ? 0 : 1));
switch (bits_per_sample[0])
{
case 1:
bit = (byte)(bit * 255);
break;
case 4:
bit = (byte)(bit * 17);
break;
}
for (int i=1; i < samples_per_pixel; i++) // skip any extra samples
for (int j=0; j < bits_per_sample[i]; j++)
bits.ReadBit();
}
if (photometric_interpretation == TIFFPhotometricInterpretation.Bilevel_BlackIsZero)
bit ^= 0xFF;
if (sixteen_bits)
for (int i=0; i < 8; i++)
bmp_data_ptr[bmp_data_offset++] = bit;
else
for (int i=0; i < 4; i++)
bmp_data_ptr[bmp_data_offset++] = bit;
}
bits.SyncToNextByte();
bmp_data_offset += bmp_fixup;
}
}
break;
}
case TIFFPhotometricInterpretation.RGB_Palette:
{
using (MemoryStream stream = new MemoryStream(bytes))
{
BitReader bits = new BitReader(stream);
for (long yy=y; yy < strip_end; yy++)
{
for (int xx=0; xx < width; xx++)
{
int idx = 0;
unchecked
{
if ((bits_per_sample[0] == 8) && (extra_samples == 0))
idx = stream.ReadByte();
else
{
for (int i=0; i < bits_per_sample[0]; i++)
idx = ((idx << 1) | (bits.ReadBit() ? 1 : 0));
for (int i=1; i < samples_per_pixel; i++) // skip any extra samples
for (int j=0; j < bits_per_sample[i]; j++)
bits.ReadBit();
}
ushort r = (ushort)colour_map[idx + (0 << (int)bits_per_sample[0])];
ushort g = (ushort)colour_map[idx + (1 << (int)bits_per_sample[0])];
ushort b = (ushort)colour_map[idx + (2 << (int)bits_per_sample[0])];
if (sixteen_bits)
{
bmp_data_ptr[bmp_data_offset++] = (byte)(b & 0xFF);
bmp_data_ptr[bmp_data_offset++] = (byte)(b >> 8);
bmp_data_ptr[bmp_data_offset++] = (byte)(g & 0xFF);
bmp_data_ptr[bmp_data_offset++] = (byte)(g >> 8);
bmp_data_ptr[bmp_data_offset++] = (byte)(r & 0xFF);
bmp_data_ptr[bmp_data_offset++] = (byte)(r >> 8);
bmp_data_ptr[bmp_data_offset++] = 0;
bmp_data_ptr[bmp_data_offset++] = 0;
}
else
{
bmp_data_ptr[bmp_data_offset++] = (byte)(b >> 8);
bmp_data_ptr[bmp_data_offset++] = (byte)(g >> 8);
bmp_data_ptr[bmp_data_offset++] = (byte)(r >> 8);
bmp_data_ptr[bmp_data_offset++] = 0;
}
}
}
bits.SyncToNextByte();
bmp_data_offset += bmp_fixup;
}
}
break;
}
case TIFFPhotometricInterpretation.RGB:
case TIFFPhotometricInterpretation.YCbCr:
case TIFFPhotometricInterpretation.CIELab:
{
using (MemoryStream stream = new MemoryStream(bytes))
{
BitReader bits = new BitReader(stream);
for (long yy=y; yy < strip_end; yy++)
{
for (int xx=0; xx < width; xx++)
{
unchecked
{
for (int i=0; i < 3; i++)
{
int chan = 0;
if (bits_per_sample[i] < 16)
{
for (int j=0; j < bits_per_sample[i]; j++)
chan = ((chan << 1) | (bits.ReadBit() ? 1 : 0));
for (int j=(int)bits_per_sample[i]; j < 16; j += (int)bits_per_sample[i])
chan = (chan << (int)bits_per_sample[i]) | chan;
if ((16 % bits_per_sample[i]) != 0)
{
chan >>= (int)(bits_per_sample[i] - (16u % bits_per_sample[i]) - 1);
chan = (chan + 1) >> 1;
}
}
else
{
for (int j=0; j < 16; j++)
chan = ((chan << 1) | (bits.ReadBit() ? 1 : 0));
for (int j=16; j < bits_per_sample[i]; j++)
bits.ReadBit(); // discard sample noise
}
if ((bits_per_sample[i] > 8) && (header.IntelByteOrder == true))
chan = (ushort)((chan >> 8) | (chan << 8));
if ((i > 0) && (photometric_interpretation == TIFFPhotometricInterpretation.CIELab))
chan = (ushort)(chan + 0x8000); // bias the 'a' and 'b' channels
if (sixteen_bits)
{
bmp_data_ptr[bmp_data_offset + (2 - i) * 2] = (byte)(chan & 0xFF);
bmp_data_ptr[bmp_data_offset + (2 - i) * 2 + 1] = (byte)(chan >> 8);
}
else
bmp_data_ptr[bmp_data_offset + (2 - i)] = (byte)(chan >> 8);
}
for (int i=3; i < samples_per_pixel; i++) // skip any extra samples
for (int j=0; j < bits_per_sample[i]; j++)
bits.ReadBit();
if (sixteen_bits)
{
bmp_data_offset += 6;
bmp_data_ptr[bmp_data_offset++] = 0;
}
else
bmp_data_offset += 3;
bmp_data_ptr[bmp_data_offset++] = 0;
}
}
bits.SyncToNextByte();
bmp_data_offset += bmp_fixup;
}
}
break;
}
case TIFFPhotometricInterpretation.CMYK:
{
using (MemoryStream stream = new MemoryStream(bytes))
{
BitReader bits = new BitReader(stream);
for (long yy=y; yy < strip_end; yy++)
{
for (int xx=0; xx < width; xx++)
{
unchecked
{
for (int i=0; i < 4; i++)
{
int chan = 0;
if (bits_per_sample[i] < 16)
{
for (int j=0; j < bits_per_sample[i]; j++)
chan = ((chan << 1) | (bits.ReadBit() ? 1 : 0));
for (int j=(int)bits_per_sample[i]; j < 16; j += (int)bits_per_sample[i])
chan = (chan << (int)bits_per_sample[i]) | chan;
if ((16 % bits_per_sample[i]) != 0)
{
chan >>= (int)(bits_per_sample[i] - (16u % bits_per_sample[i]) - 1);
chan = (chan + 1) >> 1;
}
}
else
{
for (int j=0; j < 16; j++)
chan = ((chan << 1) | (bits.ReadBit() ? 1 : 0));
for (int j=16; j < bits_per_sample[i]; j++)
bits.ReadBit(); // discard sample noise
}
if ((bits_per_sample[i] > 8) && (header.IntelByteOrder == true))
chan = (ushort)((chan >> 8) | (chan << 8));
if (sixteen_bits)
{
bmp_data_ptr[bmp_data_offset + i * 2] = (byte)(chan & 0xFF);
bmp_data_ptr[bmp_data_offset + i * 2 + 1] = (byte)(chan >> 8);
}
else
bmp_data_ptr[bmp_data_offset + i] = (byte)(chan >> 8);
}
for (int i=4; i < samples_per_pixel; i++) // skip any extra samples
for (int j=0; j < bits_per_sample[i]; j++)
bits.ReadBit();
}
}
bits.SyncToNextByte();
bmp_data_offset += bmp_fixup;
}
}
break;
}
default:
throw new OutOfMemoryException(_("Unknown photometric interpretation."));
}
}
}
finally
{
if (bmp_data != null)
bmp.UnlockBits(bmp_data);
}
TIFFImageInfo ret = new TIFFImageInfo();
ret.BitmapData = bmp;
ret.BitsPerComponent = sixteen_bits ? 16 : 8;
if (photometric_interpretation == TIFFPhotometricInterpretation.CMYK)
ret.ColourSpace = TIFFColourSpace.CMYK;
else if (photometric_interpretation == TIFFPhotometricInterpretation.CIELab)
ret.ColourSpace = TIFFColourSpace.Lab;
else
ret.ColourSpace = TIFFColourSpace.RGB;
return ret;
}
public static void SaveTIFF(Bitmap bmp, string filename, TIFFColourSpace colour_space)
{
FileStream file = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read);
try
{
SaveTIFF(bmp, file, colour_space);
}
finally
{
file.Close();
}
}
public static void SaveTIFF(Bitmap bmp, Stream seekable_stream, TIFFColourSpace colour_space)
{
SaveTIFF(bmp, seekable_stream, 0, colour_space);
}
static int bits_per_sample_for_pixel_format(PixelFormat format, int component)
{
switch (format)
{
case PixelFormat.Format1bppIndexed:
return 1;
case PixelFormat.Format4bppIndexed:
return 4;
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
if (component == 2)
return 6;
else
return 5;
case PixelFormat.Format8bppIndexed:
return 8;
case PixelFormat.Format16bppGrayScale:
return 16;
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppRgb:
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
return 8;
case PixelFormat.Format48bppRgb:
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
return 16;
default:
return 0;
}
}
static int bits_per_sample_for_pixel_format(PixelFormat format)
{
return bits_per_sample_for_pixel_format(format, 1);
}
static int samples_per_pixel_for_pixel_format(PixelFormat format, TIFFColourSpace colour_space)
{
switch (format)
{
case PixelFormat.Format1bppIndexed:
case PixelFormat.Format4bppIndexed:
case PixelFormat.Format8bppIndexed:
case PixelFormat.Format16bppGrayScale:
return 1;
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
case PixelFormat.Format24bppRgb:
case PixelFormat.Format32bppRgb:
case PixelFormat.Format48bppRgb:
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
if (colour_space == TIFFColourSpace.CMYK)
return 4;
else
return 3;
default:
return 0;
}
}
static int bits_per_pixel_for_pixel_format(PixelFormat format)
{
switch (format)
{
case PixelFormat.Format1bppIndexed:
return 1;
case PixelFormat.Format4bppIndexed:
return 4;
case PixelFormat.Format8bppIndexed:
return 8;
case PixelFormat.Format16bppGrayScale:
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
return 16;
case PixelFormat.Format24bppRgb:
return 24;
case PixelFormat.Format32bppRgb:
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
return 32;
case PixelFormat.Format48bppRgb:
return 48;
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
return 64;
default:
return 0;
}
}
const int MaximumStripSize = 8200; // as per the spec recommendation of "about 8kb"
public unsafe static void SaveTIFF(Bitmap bmp, Stream seekable_stream, int offset, TIFFColourSpace colour_space)
{
seekable_stream.Seek(offset, SeekOrigin.Begin);
BinaryWriter writer = new BinaryWriter(seekable_stream);
int tiff_pixel_bits = 0;
for (int i=1; i <= samples_per_pixel_for_pixel_format(bmp.PixelFormat, colour_space); i++)
tiff_pixel_bits += bits_per_sample_for_pixel_format(bmp.PixelFormat, i);
int tiff_stride_bits = bmp.Width * tiff_pixel_bits;
int tiff_stride_bytes = (tiff_stride_bits + 7) >> 3;
int tiff_rows_per_strip = MaximumStripSize / tiff_stride_bytes;
if (tiff_rows_per_strip == 0)
tiff_rows_per_strip = 1;
int num_strips = (bmp.Height + tiff_rows_per_strip - 1) / tiff_rows_per_strip;
int strip_size = tiff_rows_per_strip * tiff_stride_bytes;
int IFD_offset = 8;
int IFD_size = 2 + 11 * 12 + 4;
int XRes_offset = IFD_offset + IFD_size;
int XRes_size = 8;
int YRes_offset = XRes_offset + XRes_size;
int YRes_size = 8;
int[] strip_offsets = new int[num_strips];
int strip_offset_table_offset = YRes_offset + YRes_size;
int strip_offset_table_size = num_strips * 4;
if (strip_offset_table_size <= 4)
strip_offset_table_size = 0;
int strip_size_table_offset = strip_offset_table_offset + strip_offset_table_size;
int strip_size_table_size = num_strips * 4;
if (strip_size_table_size <= 4)
strip_size_table_size = 0;
for (int i=0; i < num_strips; i++)
if (i == 0)
strip_offsets[i] = strip_size_table_offset + strip_size_table_size;
else
strip_offsets[i] = strip_offsets[i - 1] + strip_size;
// the header
writer.Write((ushort)0x4949);
writer.Write((ushort)42);
writer.Write((uint)8);
// the IFD
writer.Write((ushort)11);
// ImageWidth
writer.Write((ushort)TIFFTag.ImageWidth);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)1);
writer.Write((uint)bmp.Width);
// ImageLength
writer.Write((ushort)TIFFTag.ImageLength);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)1);
writer.Write((uint)bmp.Height);
// BitsPerSample
writer.Write((ushort)TIFFTag.BitsPerSample);
writer.Write((ushort)TIFFType.Byte);
writer.Write((uint)samples_per_pixel_for_pixel_format(bmp.PixelFormat, colour_space));
for (int i=1; i <= 4; i++)
writer.Write((byte)bits_per_sample_for_pixel_format(bmp.PixelFormat, i));
// Compression
writer.Write((ushort)TIFFTag.Compression);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)1);
writer.Write((uint)TIFFCompression.Uncompressed);
// PhotometricInterpretation
writer.Write((ushort)TIFFTag.PhotometricInterpretation);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)1);
switch (colour_space)
{
case TIFFColourSpace.Lab:
writer.Write((uint)TIFFPhotometricInterpretation.CIELab);
break;
case TIFFColourSpace.CMYK:
writer.Write((uint)TIFFPhotometricInterpretation.CMYK);
break;
case TIFFColourSpace.RGB:
default:
writer.Write((uint)TIFFPhotometricInterpretation.RGB);
break;
}
// StripOffsets
writer.Write((ushort)TIFFTag.StripOffsets);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)num_strips);
if (strip_offset_table_size == 0)
writer.Write((uint)strip_offsets[0]);
else
writer.Write((uint)strip_offset_table_offset);
// SamplesPerPixel
writer.Write((ushort)TIFFTag.SamplesPerPixel);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)1);
writer.Write((uint)samples_per_pixel_for_pixel_format(bmp.PixelFormat, colour_space));
// RowsPerStrip
writer.Write((ushort)TIFFTag.RowsPerStrip);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)1);
writer.Write((uint)tiff_rows_per_strip);
// StripByteCounts
writer.Write((ushort)TIFFTag.StripByteCounts);
writer.Write((ushort)TIFFType.Long);
writer.Write((uint)num_strips);
if (strip_size_table_size == 0)
writer.Write((uint)(bmp.Height * tiff_stride_bytes));
else
writer.Write((uint)strip_size_table_offset);
// XResolution
writer.Write((ushort)TIFFTag.XResolution);
writer.Write((ushort)TIFFType.Rational);
writer.Write((uint)1);
writer.Write((uint)XRes_offset);
// YResolution
writer.Write((ushort)TIFFTag.YResolution);
writer.Write((ushort)TIFFType.Rational);
writer.Write((uint)1);
writer.Write((uint)YRes_offset);
// next IFD offset
writer.Write((uint)0);
// X resolution
writer.Write((uint)72);
writer.Write((uint)1);
// Y resolution
writer.Write((uint)72);
writer.Write((uint)1);
// strip offsets
for (int i=0; i < num_strips; i++)
writer.Write(strip_offsets[i]);
// strip sizes
for (int i=0; i < num_strips; i++)
if (i + 1 < num_strips)
writer.Write(strip_size);
else
writer.Write((uint)((((bmp.Height - 1) % tiff_rows_per_strip) + 1) * tiff_stride_bytes));
// strip data
byte[] stride_buf = new byte[tiff_stride_bytes];
BitmapData data = null;
try
{
data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
byte *data_ptr = (byte *)data.Scan0.ToPointer();
int data_stride = (bits_per_pixel_for_pixel_format(data.PixelFormat) * data.Width + 7) >> 3;
int pixel_bytes = bits_per_pixel_for_pixel_format(data.PixelFormat) >> 3;
if ((bits_per_pixel_for_pixel_format(data.PixelFormat) <= 8) || (colour_space == TIFFColourSpace.CMYK))
for (int y=0; y < bmp.Height; y++)
{
int o = y * data.Stride;
for (int p=0; (p < data_stride) && (p < stride_buf.Length); p++)
stride_buf[p] = data_ptr[o + p];
writer.Write(stride_buf);
}
else if (colour_space == TIFFColourSpace.Lab)
{
if (bits_per_sample_for_pixel_format(data.PixelFormat) == 16)
for (int y=0; y < bmp.Height; y++)
{
int o = y * data.Stride;
for (int x=0, p=0; x < bmp.Width; x++, p += pixel_bytes)
for (int i=0; i < 6; i++)
{
int j = i ^ 4 ^ ((i & 2) << 1); // {0,1,2,3,4,5} -> {4,5,2,3,0,1}
if ((i == 3) || (i == 5))
stride_buf[x * 6 + j] = (byte)(0x80 ^ data_ptr[o + p + i]); // unbias
else
stride_buf[x * 6 + j] = data_ptr[o + p + i];
}
writer.Write(stride_buf);
}
else
for (int y=0; y < bmp.Height; y++)
{
int o = y * data.Stride;
for (int x=0, p=0; x < bmp.Width; x++, p += pixel_bytes)
{
stride_buf[x * 3 + 0] = data_ptr[o + p + 2];
stride_buf[x * 3 + 1] = (byte)(0x80 ^ data_ptr[o + p + 1]);
stride_buf[x * 3 + 2] = (byte)(0x80 ^ data_ptr[o + p + 0]);
}
writer.Write(stride_buf);
}
}
else if (bits_per_sample_for_pixel_format(data.PixelFormat) == 16)
for (int y=0; y < bmp.Height; y++)
{
int o = y * data.Stride;
for (int x=0, p=0; x < bmp.Width; x++, p += pixel_bytes)
for (int i=0; i < 6; i++)
{
int j = i ^ 4 ^ ((i & 2) << 1); // {0,1,2,3,4,5} -> {4,5,2,3,0,1}
stride_buf[x * 6 + j] = data_ptr[o + p + i];
}
writer.Write(stride_buf);
}
else
for (int y=0; y < bmp.Height; y++)
{
int o = y * data.Stride;
for (int x=0, p=0; x < bmp.Width; x++, p += pixel_bytes)
for (int i=0; i < 3; i++)
stride_buf[x * 3 + i] = data_ptr[o + p + 2 - i];
writer.Write(stride_buf);
}
}
finally
{
if (data != null)
bmp.UnlockBits(data);
}
}
static string _(string key)
{
return Language.Item[key];
}
}
class MSBBinaryReader : BinaryReader
{
public MSBBinaryReader(Stream input)
: base(input)
{
}
public MSBBinaryReader(Stream input, Encoding encoding)
: base(input, encoding)
{
}
public override decimal ReadDecimal()
{
int[] bits = new int[4];
bits[3] = ReadInt32();
bits[2] = ReadInt32();
bits[1] = ReadInt32();
bits[0] = ReadInt32();
return new decimal(bits);
}
public override double ReadDouble()
{
double[] item = new double[1];
for (int i=0; i < 8; i++)
Buffer.SetByte(item, 7 - i, ReadByte());
return item[0];
}
public override short ReadInt16()
{
return unchecked((short)ReadUInt16());
}
public override int ReadInt32()
{
return unchecked((int)ReadUInt32());
}
public override long ReadInt64()
{
return unchecked((int)ReadUInt64());
}
public override float ReadSingle()
{
float[] item = new float[1];
for (int i=0; i < 4; i++)
Buffer.SetByte(item, 3 - i, ReadByte());
return item[0];
}
public override ushort ReadUInt16()
{
ushort left = ReadByte();
byte right = ReadByte();
return unchecked((ushort)((left << 8) | right));
}
public override uint ReadUInt32()
{
uint left = ReadUInt16();
ushort right = ReadUInt16();
return unchecked((uint)((left << 16) | right));
}
public override ulong ReadUInt64()
{
ulong left = ReadUInt32();
uint right = ReadUInt32();
return unchecked((left << 32) | right);
}
}
}
More information about the Mono-list
mailing list