diff --git a/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GraphicDesigner/Associations/Picture/TIFF.uexml b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GraphicDesigner/Associations/Picture/TIFF.uexml
new file mode 100644
index 00000000..be44944c
--- /dev/null
+++ b/Content/UniversalEditor.Content.PlatformIndependent/Extensions/GraphicDesigner/Associations/Picture/TIFF.uexml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+ *.tif
+ *.tiff
+
+
+
+ II
+
+
+ MM
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj b/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj
index 1cdc560a..4bc2260c 100644
--- a/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj
+++ b/Content/UniversalEditor.Content.PlatformIndependent/UniversalEditor.Content.PlatformIndependent.csproj
@@ -743,6 +743,7 @@
+
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFCompression.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFCompression.cs
new file mode 100644
index 00000000..31273757
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFCompression.cs
@@ -0,0 +1,45 @@
+//
+// TIFFCompression.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFCompression : ushort
+ {
+ ///
+ /// No compression, but pack data into bytes as tightly as possible, leaving no unused bits (except at the end of a row). The component values are stored as an array of
+ /// type BYTE. Each scan line (row) is padded to the next BYTE boundary.
+ ///
+ None = 0x01,
+ ///
+ /// CCITT Group 3 1-Dimensional Modified Huffman run length encoding.
+ ///
+ RunLengthEncoding = 0x02,
+ Group3Fax = 0x03,
+ Group4Fax = 0x04,
+ LZW = 0x05,
+ JPEG = 0x06,
+ Deflate = 0x08,
+ ///
+ /// PackBits compression, a simple byte-oriented run length scheme.
+ ///
+ PackBits = 32773
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataFormat.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataFormat.cs
new file mode 100644
index 00000000..0a640d07
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataFormat.cs
@@ -0,0 +1,177 @@
+//
+// TIFFDataFormat.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.Collections.Generic;
+
+using UniversalEditor.IO;
+using UniversalEditor.ObjectModels.Multimedia.Picture;
+using UniversalEditor.ObjectModels.Multimedia.Picture.Collection;
+
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public class TIFFDataFormat : TIFFDataFormatBase
+ {
+ private static DataFormatReference _dfr = null;
+ protected override DataFormatReference MakeReferenceInternal()
+ {
+ if (_dfr == null)
+ {
+ _dfr = base.MakeReferenceInternal();
+ _dfr.Type = GetType();
+
+ _dfr.Capabilities.Clear();
+ _dfr.Capabilities.Add(typeof(PictureCollectionObjectModel), DataFormatCapabilities.All);
+
+ _dfr.ExportOptions.Add(new CustomOptionChoice("CompressionMethod", "Compression _method", true, new CustomOptionFieldChoice[]
+ {
+ new CustomOptionFieldChoice("None", TIFFCompression.None),
+ new CustomOptionFieldChoice("Run-length encoding", TIFFCompression.RunLengthEncoding),
+ new CustomOptionFieldChoice("Group 3 fax", TIFFCompression.Group3Fax),
+ new CustomOptionFieldChoice("Group 4 fax", TIFFCompression.Group4Fax),
+ new CustomOptionFieldChoice("LZW", TIFFCompression.LZW),
+ new CustomOptionFieldChoice("JPEG", TIFFCompression.JPEG),
+ new CustomOptionFieldChoice("Deflate", TIFFCompression.Deflate),
+ new CustomOptionFieldChoice("PackBits", TIFFCompression.PackBits)
+ }));
+ }
+ return _dfr;
+ }
+
+ public TIFFCompression CompressionMethod { get; set; } = TIFFCompression.None;
+
+ protected override void BeforeLoadInternal(Stack objectModels)
+ {
+ base.BeforeLoadInternal(objectModels);
+ objectModels.Push(new TIFFObjectModelBase());
+ }
+ protected override void AfterLoadInternal(Stack objectModels)
+ {
+ base.AfterLoadInternal(objectModels);
+
+ TIFFObjectModelBase tiff = (objectModels.Pop() as TIFFObjectModelBase);
+ PictureCollectionObjectModel picc = (objectModels.Pop() as PictureCollectionObjectModel);
+
+ Reader reader = Accessor.Reader;
+
+ for (int i = 0; i < tiff.ImageFileDirectories.Count; i++)
+ {
+ PictureObjectModel pic = new PictureObjectModel();
+
+ TIFFImageFileDirectory ifd = tiff.ImageFileDirectories[i];
+
+ TIFFImageFileDirectoryEntry entryWidth = ifd.Entries.GetByTag(TIFFTag.ImageWidth);
+ TIFFImageFileDirectoryEntry entryHeight = ifd.Entries.GetByTag(TIFFTag.ImageLength);
+ if (entryWidth == null || entryHeight == null)
+ {
+ Console.WriteLine("tiff: skipping invalid IFD which does not contain both ImageWidth and ImageLength fields");
+ continue;
+ }
+
+ long width = entryWidth.GetNumericValue(), height = entryHeight.GetNumericValue();
+
+ TIFFImageFileDirectoryEntry entryCompression = ifd.Entries.GetByTag(TIFFTag.Compression);
+ if (entryCompression != null)
+ {
+ CompressionMethod = (TIFFCompression)entryCompression.GetNumericValue();
+ }
+
+ pic.Width = (int)width;
+ pic.Height = (int)height;
+
+ TIFFImageFileDirectoryEntry entryRowsPerStrip = ifd.Entries.GetByTag(TIFFTag.RowsPerStrip);
+ // The number of rows in each strip (except possibly the last strip.)
+ // For example, if ImageLength is 24, and RowsPerStrip is 10, then there are 3 strips, with 10 rows in the first strip, 10 rows in the second strip,
+ // and 4 rows in the third strip. (The data in the last strip is not padded with 6 extra rows of dummy data.)
+ long rowsPerStrip = entryRowsPerStrip.GetNumericValue();
+
+ long rpsEvenLength = (long)((double)pic.Height / rowsPerStrip);
+ long remaining = (pic.Height - (rpsEvenLength * rowsPerStrip));
+
+ TIFFImageFileDirectoryEntry entryStripOffsets = ifd.Entries.GetByTag(TIFFTag.StripOffsets);
+ if (entryStripOffsets == null)
+ {
+ continue;
+ }
+
+ uint[] stripOffsets = (uint[])entryStripOffsets.Value;
+
+ // 288000 bytes in strip
+ // 288000 / 128 rows per strip = 2250 bytes per row
+ // 2250 / 750 (Width) = 3 bytes per pixel
+
+ int yoffset = 0;
+ for (uint j = 0; j < stripOffsets.Length; j++)
+ {
+ // for each strip
+ uint stripOffset = stripOffsets[j];
+ reader.Seek(stripOffset, SeekOrigin.Begin);
+
+ long rowcount = rowsPerStrip;
+ if (j == stripOffsets.Length - 1 && remaining > 0)
+ {
+ rowcount = remaining;
+ }
+
+ for (int y = 0; y < rowcount; y++)
+ {
+ for (int x = 0; x < pic.Width; x++)
+ {
+ byte pixelR = reader.ReadByte();
+ byte pixelG = reader.ReadByte();
+ byte pixelB = reader.ReadByte();
+ pic.SetPixel(MBS.Framework.Drawing.Color.FromRGBAByte(pixelR, pixelG, pixelB), x, yoffset + y);
+ }
+ }
+ yoffset += (int)rowcount;
+ reader.Align(4);
+ }
+
+ picc.Pictures.Add(pic);
+ }
+ }
+
+ protected override void BeforeSaveInternal(Stack objectModels)
+ {
+ base.BeforeSaveInternal(objectModels);
+
+ PictureCollectionObjectModel picc = (objectModels.Pop() as PictureCollectionObjectModel);
+ TIFFObjectModelBase tiff = new TIFFObjectModelBase();
+
+ for (int i = 0; i < picc.Pictures.Count; i++)
+ {
+ PictureObjectModel pic = picc.Pictures[i];
+
+ TIFFImageFileDirectory ifd = new TIFFImageFileDirectory();
+
+ ifd.Entries.Add(TIFFTag.ImageWidth, pic.Width);
+ ifd.Entries.Add(TIFFTag.ImageLength, pic.Height);
+ ifd.Entries.Add(TIFFTag.Compression, (ushort)CompressionMethod);
+
+ ifd.Entries.Add(TIFFTag.RowsPerStrip, 128);
+
+ tiff.ImageFileDirectories.Add(ifd);
+ }
+
+ objectModels.Push(tiff);
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataFormatBase.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataFormatBase.cs
new file mode 100644
index 00000000..0e160ee2
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataFormatBase.cs
@@ -0,0 +1,482 @@
+//
+// TIFFDataFormatBase.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using System.Collections.Generic;
+using UniversalEditor.Accessors;
+using UniversalEditor.IO;
+
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public class TIFFDataFormatBase : DataFormat
+ {
+ private static DataFormatReference _dfr = null;
+ protected override DataFormatReference MakeReferenceInternal()
+ {
+ if (_dfr == null)
+ {
+ _dfr = base.MakeReferenceInternal();
+ _dfr.Capabilities.Add(typeof(TIFFObjectModelBase), DataFormatCapabilities.All);
+ _dfr.ExportOptions.Add(new CustomOptionChoice("Endianness", "_Endianness", true, new CustomOptionFieldChoice[]
+ {
+ new CustomOptionFieldChoice("Little-endian", Endianness.LittleEndian),
+ new CustomOptionFieldChoice("Big-endian", Endianness.BigEndian)
+ }));
+ }
+ return _dfr;
+ }
+
+ public Endianness Endianness { get; set; } = Endianness.LittleEndian;
+
+ protected override void LoadInternal(ref ObjectModel objectModel)
+ {
+ TIFFObjectModelBase tiff = (objectModel as TIFFObjectModelBase);
+ if (tiff == null) throw new ObjectModelNotSupportedException();
+
+ Reader reader = Accessor.Reader;
+ string signature1 = reader.ReadFixedLengthString(2);
+ if (signature1 == "II")
+ {
+ reader.Endianness = Endianness.LittleEndian;
+ }
+ else if (signature1 == "MM")
+ {
+ reader.Endianness = Endianness.BigEndian;
+ }
+ else
+ {
+ throw new InvalidDataFormatException("file does not begin with either 'II' or 'MM'");
+ }
+
+ Endianness = reader.Endianness;
+
+ ushort signature2 = reader.ReadUInt16();
+ if (signature2 != 42)
+ {
+ throw new InvalidDataFormatException("incorrect answer to the ultimate question of life, the universe, and everything (expected 42)");
+ }
+
+ while (!reader.EndOfStream)
+ {
+ // The offset (in bytes) of the first IFD. The directory may be at any location in the
+ // file after the header but must begin on a word boundary.In particular, an Image
+ // File Directory may follow the image data it describes. Readers must follow the
+ // pointers wherever they may lead.
+ uint IFDoffset = reader.ReadUInt32();
+ if (IFDoffset == 0)
+ break;
+
+ reader.Seek(IFDoffset, SeekOrigin.Begin);
+
+ TIFFImageFileDirectory ifd = new TIFFImageFileDirectory();
+
+ ushort directoryEntryCount = reader.ReadUInt16();
+ ReadIFDEntries(reader, ifd, directoryEntryCount);
+
+ tiff.ImageFileDirectories.Add(ifd);
+ }
+ }
+
+ private void ReadIFDEntries(Reader reader, TIFFImageFileDirectory ifd, ushort count)
+ {
+ // There must be at least 1 IFD in a TIFF file and each IFD must have at least one entry.
+ for (ushort i = 0; i < count; i++)
+ {
+ ifd.Entries.Add(ReadIFDEntry(reader));
+ }
+ }
+ private TIFFImageFileDirectoryEntry ReadIFDEntry(Reader reader)
+ {
+ // An Image File Directory (IFD) consists of a 2-byte count of the number of directory
+ // entries(i.e., the number of fields), followed by a sequence of 12 - byte field
+ // entries, followed by a 4 - byte offset of the next IFD(or 0 if none). (Do not forget to
+ // write the 4 bytes of 0 after the last IFD.)
+
+ TIFFImageFileDirectoryEntry entry = new TIFFImageFileDirectoryEntry();
+
+ entry.Tag = (TIFFTag)reader.ReadUInt16();
+ entry.Type = (TIFFDataType)reader.ReadUInt16();
+ entry.Count = reader.ReadUInt32();
+ entry.OffsetOrValue = reader.ReadUInt32();
+
+ byte[] value = BitConverter.GetBytes(entry.OffsetOrValue);
+
+ long ofs = reader.Accessor.Position;
+
+ switch (entry.Type)
+ {
+ case TIFFDataType.Ascii:
+ {
+ if (entry.Count <= 4)
+ {
+ entry.Value = System.Text.Encoding.Default.GetString(value);
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadFixedLengthString((int)entry.Count);
+ }
+ break;
+ }
+ case TIFFDataType.Undefined:
+ {
+ if (entry.Count <= 4)
+ {
+ entry.Value = value;
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadBytes((int)entry.Count);
+ }
+ break;
+ }
+ case TIFFDataType.Byte:
+ case TIFFDataType.SignedByte:
+ {
+ if (entry.Count == 1)
+ {
+ entry.Value = value[0];
+ break;
+ }
+ else if (entry.Count <= 4)
+ {
+ byte[] val = new byte[entry.Count];
+ for (int i = 0; i < entry.Count; i++)
+ {
+ val[i] = value[i];
+ }
+ entry.Value = val;
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadBytes((int)entry.Count);
+ }
+ break;
+ }
+ case TIFFDataType.Short:
+ {
+ if (entry.Count == 1)
+ {
+ entry.Value = BitConverter.ToUInt16(value, 0);
+ }
+ else if (entry.Count == 2)
+ {
+ entry.Value = new ushort[] { BitConverter.ToUInt16(value, 0), BitConverter.ToUInt16(value, 2) };
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadUInt16Array((int)entry.Count);
+ }
+ break;
+ }
+ case TIFFDataType.SignedShort:
+ {
+ if (entry.Count == 1)
+ {
+ entry.Value = BitConverter.ToInt16(value, 0);
+ }
+ else if (entry.Count == 2)
+ {
+ entry.Value = new short[] { BitConverter.ToInt16(value, 0), BitConverter.ToInt16(value, 2) };
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadInt16Array((int)entry.Count);
+ }
+ break;
+ }
+ case TIFFDataType.Long:
+ {
+ if (entry.Count == 1)
+ {
+ entry.Value = BitConverter.ToUInt32(value, 0);
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadUInt32Array((int)entry.Count);
+ }
+ break;
+ }
+ case TIFFDataType.SignedLong:
+ {
+ if (entry.Count == 1)
+ {
+ entry.Value = BitConverter.ToInt32(value, 0);
+ }
+ else
+ {
+ reader.Accessor.Position = entry.OffsetOrValue;
+ entry.Value = reader.ReadInt32Array((int)entry.Count);
+ }
+ break;
+ }
+ }
+
+ reader.Accessor.Position = ofs;
+ return entry;
+ }
+
+ protected override void SaveInternal(ObjectModel objectModel)
+ {
+ TIFFObjectModelBase tiff = (objectModel as TIFFObjectModelBase);
+ if (tiff == null) throw new ObjectModelNotSupportedException();
+
+ Writer writer = Accessor.Writer;
+ writer.Endianness = Endianness;
+
+ switch (Endianness)
+ {
+ case Endianness.LittleEndian:
+ {
+ writer.WriteFixedLengthString("II");
+ break;
+ }
+ case Endianness.BigEndian:
+ {
+ writer.WriteFixedLengthString("MM");
+ break;
+ }
+ default:
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ writer.WriteUInt16(42); // signature2
+
+ uint IFDoffset = 8;
+
+ for (int i = 0; i < tiff.ImageFileDirectories.Count; i++)
+ {
+ writer.WriteUInt32(IFDoffset);
+
+ TIFFImageFileDirectory ifd = tiff.ImageFileDirectories[i];
+ WriteIFDEntries(writer, ifd);
+
+ IFDoffset = (uint)writer.Accessor.Position + 4;
+ }
+ }
+
+ private void WriteIFDEntries(Writer writer, TIFFImageFileDirectory ifd)
+ {
+ // There must be at least 1 IFD in a TIFF file and each IFD must have at least one entry.
+ writer.WriteUInt16((ushort)ifd.Entries.Count);
+
+ List ifdentries = new List(ifd.Entries);
+ ifdentries.Sort();
+
+ MemoryAccessor maOverflow = new MemoryAccessor();
+ Writer overflowWriter = maOverflow.Writer;
+
+ long overflowWriterOffset = Accessor.Position + ((ushort)ifdentries.Count * 12);
+ for (ushort i = 0; i < (ushort)ifdentries.Count; i++)
+ {
+ WriteIFDEntry(writer, ifdentries[i], overflowWriter, overflowWriterOffset);
+ }
+
+ writer.WriteBytes(maOverflow.ToArray());
+ }
+
+ private void WriteIFDEntry(Writer writer, TIFFImageFileDirectoryEntry entry, Writer overflowWriter, long overflowWriterOffset)
+ {
+ // An Image File Directory (IFD) consists of a 2-byte count of the number of directory
+ // entries(i.e., the number of fields), followed by a sequence of 12 - byte field
+ // entries, followed by a 4 - byte offset of the next IFD(or 0 if none). (Do not forget to
+ // write the 4 bytes of 0 after the last IFD.)
+ long overflowOffset = overflowWriter.Accessor.Position;
+
+ if (entry.Value is string)
+ {
+ entry.Type = TIFFDataType.Ascii;
+ string value = (string)entry.Value;
+ entry.Count = (uint)value.Length;
+ if (value.Length <= 4)
+ {
+ entry.OffsetOrValue = BitConverter.ToUInt32(System.Text.Encoding.Default.GetBytes(value), 0);
+ }
+ else
+ {
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteFixedLengthString((string)entry.Value);
+ }
+ }
+ else if (entry.Value is byte[] && entry.Type == TIFFDataType.Undefined)
+ {
+ byte[] value = (byte[])entry.Value;
+ entry.Count = (uint)value.Length;
+ if (value.Length <= 4)
+ {
+ entry.OffsetOrValue = BitConverter.ToUInt32(value, 0);
+ }
+ else
+ {
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteBytes(value);
+ }
+ }
+ else if ((entry.Value is byte || entry.Value is sbyte || entry.Value is byte[] || entry.Value is sbyte[]) && (entry.Type == TIFFDataType.Byte || entry.Type == TIFFDataType.SignedByte))
+ {
+ if (entry.Value is byte)
+ {
+ entry.Type = TIFFDataType.Byte;
+ entry.Count = 1;
+ entry.OffsetOrValue = (byte)entry.Value;
+ }
+ else if (entry.Value is byte[] && ((byte[])entry.Value).Length <= 4)
+ {
+ entry.Type = TIFFDataType.Byte;
+ byte[] value = (byte[])entry.Value;
+ entry.Count = (uint)value.Length;
+ entry.OffsetOrValue = BitConverter.ToUInt32(value, 0);
+ }
+ else if (entry.Value is byte[])
+ {
+ entry.Type = TIFFDataType.Byte;
+ entry.Count = (uint)((byte[])entry.Value).Length;
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteBytes((byte[])entry.Value);
+ }
+ else if (entry.Value is sbyte)
+ {
+ entry.Type = TIFFDataType.SignedByte;
+ entry.Count = 1;
+ entry.OffsetOrValue = (uint)(sbyte)entry.Value;
+ }
+ else if (entry.Value is sbyte[] && ((sbyte[])entry.Value).Length <= 4)
+ {
+ entry.Type = TIFFDataType.SignedByte;
+ sbyte[] value = (sbyte[])entry.Value;
+ entry.Count = (uint)value.Length;
+ entry.OffsetOrValue = BitConverter.ToUInt32((byte[])(Array)value, 0);
+ }
+ else if (entry.Value is sbyte[])
+ {
+ entry.Type = TIFFDataType.SignedByte;
+ entry.Count = (uint)((sbyte[])entry.Value).Length;
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteSBytes((sbyte[])entry.Value);
+ }
+ }
+ else if (entry.Value is ushort || entry.Value is ushort[])
+ {
+ entry.Type = TIFFDataType.Short;
+ if (entry.Value is ushort)
+ {
+ entry.Count = 1;
+ entry.OffsetOrValue = (ushort)entry.Value;
+ }
+ else if (entry.Value is ushort[] && ((ushort[])entry.Value).Length == 2)
+ {
+ entry.Count = 2;
+ byte[] data = new byte[4];
+ ushort[] items = (ushort[])entry.Value;
+
+ if (items.Length != 2)
+ throw new InvalidOperationException();
+
+ byte[] u1 = BitConverter.GetBytes(items[0]);
+ byte[] u2 = BitConverter.GetBytes(items[1]);
+ Array.Copy(u1, 0, data, 0, u1.Length);
+ Array.Copy(u2, 0, data, 2, u2.Length);
+
+ entry.OffsetOrValue = BitConverter.ToUInt32(data, 0);
+ }
+ else
+ {
+ entry.Count = (uint)((ushort[])entry.Value).Length;
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteUInt16Array((ushort[])entry.Value);
+ }
+ }
+ else if (entry.Value is short || entry.Value is short[])
+ {
+ entry.Type = TIFFDataType.SignedShort;
+ if (entry.Value is short)
+ {
+ entry.Count = 1;
+ entry.OffsetOrValue = (uint)(short)entry.Value;
+ }
+ else if (entry.Value is short[] && ((short[])entry.Value).Length == 2)
+ {
+ entry.Count = 2;
+
+ byte[] data = new byte[4];
+ short[] items = (short[])entry.Value;
+
+ if (items.Length != 2)
+ throw new InvalidOperationException();
+
+ byte[] u1 = BitConverter.GetBytes(items[0]);
+ byte[] u2 = BitConverter.GetBytes(items[1]);
+ Array.Copy(u1, 0, data, 0, u1.Length);
+ Array.Copy(u2, 0, data, 2, u2.Length);
+
+ entry.OffsetOrValue = BitConverter.ToUInt32(data, 0);
+ }
+ else
+ {
+ entry.Count = (uint)((short[])entry.Value).Length;
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteInt16Array((short[])entry.Value);
+ }
+ }
+ else if (entry.Value is uint || entry.Value is uint[])
+ {
+ entry.Type = TIFFDataType.Long;
+ if (entry.Value is uint)
+ {
+ entry.Count = 1;
+ entry.OffsetOrValue = (uint)entry.Value;
+ }
+ else if (entry.Value is uint[])
+ {
+ entry.Count = (uint)((uint[])entry.Value).Length;
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteUInt32Array((uint[])entry.Value);
+ }
+ }
+ else if (entry.Value is int || entry.Value is int[])
+ {
+ entry.Type = TIFFDataType.SignedLong;
+ if (entry.Value is int)
+ {
+ entry.Count = 1;
+ entry.OffsetOrValue = (uint)((int)entry.Value);
+ }
+ else if (entry.Value is int[])
+ {
+ entry.Count = (uint)((int[])entry.Value).Length;
+ entry.OffsetOrValue = (uint)(overflowWriterOffset + overflowWriter.Accessor.Position);
+ overflowWriter.WriteInt32Array((int[])entry.Value);
+ }
+ }
+
+ writer.WriteUInt16((ushort)entry.Tag);
+ writer.WriteUInt16((ushort)entry.Type);
+ writer.WriteUInt32(entry.Count);
+ writer.WriteUInt32(entry.OffsetOrValue);
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataType.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataType.cs
new file mode 100644
index 00000000..a095416a
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFDataType.cs
@@ -0,0 +1,76 @@
+//
+// TIFFDataType.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFDataType : ushort
+ {
+ ///
+ /// 8-bit unsigned integer.
+ ///
+ Byte = 0x0001,
+ ///
+ /// 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero).
+ ///
+ Ascii = 0x0002,
+ ///
+ /// 16-bit (2-byte) unsigned integer.
+ ///
+ Short = 0x0003,
+ ///
+ /// 32-bit (4-byte) unsigned integer.
+ ///
+ Long = 0x0004,
+ ///
+ /// Two s: the first represents the numerator of a fraction; the second, the denominator.
+ ///
+ Rational = 0x0005,
+
+ ///
+ /// An 8-bit signed (twos-complement) integer. Added in TIFF 6.0.
+ ///
+ SignedByte = 0x0006,
+ ///
+ /// An 8-bit byte that may contain anything, depending on the definition of the field. Added in TIFF 6.0.
+ ///
+ Undefined = 0x0007,
+ ///
+ /// A 16-bit (2-byte) signed (twos-complement) integer. Added in TIFF 6.0.
+ ///
+ SignedShort = 0x0008,
+ ///
+ /// A 32-bit (4-byte) signed (twos-complement) integer. Added in TIFF 6.0.
+ ///
+ SignedLong = 0x0009,
+ ///
+ /// Two SLONG’s: the first represents the numerator of a fraction, the second the denominator. Added in TIFF 6.0.
+ ///
+ SignedRational = 0x000A,
+ ///
+ /// Single precision (4-byte) IEEE format. Added in TIFF 6.0.
+ ///
+ Float = 0x000B,
+ ///
+ /// Double precision (8-byte) IEEE format. Added in TIFF 6.0.
+ ///
+ Double = 0x000C
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFExtraSamplesType.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFExtraSamplesType.cs
new file mode 100644
index 00000000..aee0f399
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFExtraSamplesType.cs
@@ -0,0 +1,41 @@
+//
+// TIFFExtraSamplesType.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFExtraSamplesType : ushort
+ {
+ ///
+ /// Unspecified data
+ ///
+ Unspecified = 0x00,
+ ///
+ /// Associated alpha data (with pre-multiplied color). Note that including both unassociated and associated alpha is undefined because associated alpha specifies that
+ /// color components are pre-multiplied by the alpha component, while unassociated alpha specifies the opposite.
+ ///
+ AssociatedAlpha = 0x01,
+ ///
+ /// Transparency information that logically exists independent of an image; it is commonly called a soft matte. Note that including both unassociated and associated alpha
+ /// is undefined because associated alpha specifies that color components are pre-multiplied by the alpha component, while unassociated alpha specifies the opposite.
+ ///
+ UnassociatedAlpha = 0x02,
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFFillOrder.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFFillOrder.cs
new file mode 100644
index 00000000..de4d1227
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFFillOrder.cs
@@ -0,0 +1,40 @@
+//
+// TIFFFillOrder.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFFillOrder : ushort
+ {
+ ///
+ /// Pixels are arranged within a byte such that pixels with lower column values are stored in the higher-order bits of the byte. 1-bit uncompressed data example: Pixel 0
+ /// of a row is stored in the high-order bit of byte 0, pixel 1 is stored in the next-highest bit, ..., pixel 7 is stored in the low-order bit of byte 0, pixel 8 is
+ /// stored in the high-order bit of byte 1, and so on. CCITT 1-bit compressed data example: The high-order bit of the first compression code is stored in the high-order
+ /// bit of byte 0, the next-highest bit of the first compression code is stored in the next-highest bit of byte 0, and so on.
+ ///
+ Normal = 1,
+ ///
+ /// Pixels are arranged within a byte such that pixels with lower column values are stored in the lower-order bits of the byte. We recommend that FillOrder = 2 be used
+ /// only in special-purpose applications. It is easy and inexpensive for writers to reverse bit order by using a 256-byte lookup table. FillOrder = 2 should be used only
+ /// when BitsPerSample = 1 and the data is either uncompressed or compressed using CCITT 1D or 2D compression, to avoid potentially ambiguous situations.
+ ///
+ Reverse = 2
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFImageFileDirectory.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFImageFileDirectory.cs
new file mode 100644
index 00000000..bc302cc2
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFImageFileDirectory.cs
@@ -0,0 +1,44 @@
+//
+// TIFFImageFile.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public class TIFFImageFileDirectory : ICloneable
+ {
+ public class TIFFImageFileDirectoryCollection
+ : System.Collections.ObjectModel.Collection
+ {
+
+ }
+
+ public TIFFImageFileDirectoryEntry.TIFFImageFileDirectoryEntryCollection Entries { get; } = new TIFFImageFileDirectoryEntry.TIFFImageFileDirectoryEntryCollection();
+
+ public object Clone()
+ {
+ TIFFImageFileDirectory clone = new TIFFImageFileDirectory();
+ for (int i = 0; i < Entries.Count; i++)
+ {
+ clone.Entries.Add(Entries[i].Clone() as TIFFImageFileDirectoryEntry);
+ }
+ return clone;
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFImageFileDirectoryEntry.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFImageFileDirectoryEntry.cs
new file mode 100644
index 00000000..45a4b4a0
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFImageFileDirectoryEntry.cs
@@ -0,0 +1,190 @@
+//
+// TIFFImageFileDirectory.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public class TIFFImageFileDirectoryEntry : ICloneable, IComparable
+ {
+ public class TIFFImageFileDirectoryEntryCollection
+ : System.Collections.ObjectModel.Collection
+ {
+ public TIFFImageFileDirectoryEntry GetByTag(TIFFTag tag)
+ {
+ for (int i = 0; i < Count; i++)
+ {
+ if (this[i].Tag == tag)
+ return this[i];
+ }
+ return null;
+ }
+
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, int value)
+ {
+ if (value <= short.MaxValue)
+ {
+ return Add(tag, (short)value);
+ }
+
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.SignedLong;
+ item.Value = value;
+ item.Count = 1;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, short value)
+ {
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.SignedShort;
+ item.Value = value;
+ item.Count = 1;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, uint value)
+ {
+ if (value <= ushort.MaxValue)
+ {
+ return Add(tag, (ushort)value);
+ }
+
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.Long;
+ item.Value = value;
+ item.Count = 1;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, ushort value)
+ {
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.Short;
+ item.Value = value;
+ item.Count = 1;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, int[] values)
+ {
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.SignedLong;
+ item.Value = values;
+ item.Count = (uint)values.Length;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, short[] values)
+ {
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.SignedShort;
+ item.Value = values;
+ item.Count = (uint)values.Length;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, uint[] values)
+ {
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.Long;
+ item.Value = values;
+ item.Count = (uint)values.Length;
+ Add(item);
+ return item;
+ }
+ public TIFFImageFileDirectoryEntry Add(TIFFTag tag, ushort[] values)
+ {
+ TIFFImageFileDirectoryEntry item = new TIFFImageFileDirectoryEntry();
+ item.Tag = tag;
+ item.Type = TIFFDataType.Short;
+ item.Value = values;
+ item.Count = (uint)values.Length;
+ Add(item);
+ return item;
+ }
+ }
+
+ public long GetNumericValue()
+ {
+ switch (Type)
+ {
+ case TIFFDataType.Byte: return (byte)Value;
+ case TIFFDataType.Short: return (ushort)Value;
+ case TIFFDataType.Long: return (uint)Value;
+ case TIFFDataType.SignedByte: return (sbyte)Value;
+ case TIFFDataType.SignedShort: return (short)Value;
+ case TIFFDataType.SignedLong: return (int)Value;
+ }
+ throw new NotSupportedException();
+ }
+
+ ///
+ /// The Tag that identifies the field.
+ ///
+ public TIFFTag Tag { get; set; }
+ ///
+ /// The field Type.
+ ///
+ public TIFFDataType Type { get; set; }
+ ///
+ /// The number of values, Count of the indicated Type.
+ ///
+ public uint Count { get; set; } = 1;
+ ///
+ /// The Value Offset, the file offset (in bytes) of the Value for the field. The Value is expected to begin on a word boundary; the corresponding
+ /// Value Offset will thus be an even number. This file offset may point anywhere in the file, even after the image data.
+ ///
+ ///
+ /// To save time and space the Value Offset contains the Value instead of pointing to the Value IF AND ONLY IF the Value fits into 4 bytes. If the Value is shorter
+ /// than 4 bytes, it is left-justified within the 4-byte Value Offset, i.e., stored in the lower-numbered bytes. Whether the Value fits within 4 bytes is determined by
+ /// the Type and Count of the field.
+ ///
+ public uint OffsetOrValue { get; set; }
+
+ public object Value { get; set; }
+
+ public override string ToString()
+ {
+ return String.Format("{0} ({1} x {2}) @ {3}", Tag, Type, Count, OffsetOrValue);
+ }
+
+ public object Clone()
+ {
+ TIFFImageFileDirectoryEntry clone = new TIFFImageFileDirectoryEntry();
+ clone.Tag = Tag;
+ clone.Type = Type;
+ clone.Count = Count;
+ clone.OffsetOrValue = OffsetOrValue;
+ return clone;
+ }
+
+ public int CompareTo(TIFFImageFileDirectoryEntry other)
+ {
+ return Tag.CompareTo(other.Tag);
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFObjectModelBase.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFObjectModelBase.cs
new file mode 100644
index 00000000..22698d3c
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFObjectModelBase.cs
@@ -0,0 +1,44 @@
+//
+// TIFFObjectModelBase.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public class TIFFObjectModelBase : ObjectModel
+ {
+ public TIFFImageFileDirectory.TIFFImageFileDirectoryCollection ImageFileDirectories { get; } = new TIFFImageFileDirectory.TIFFImageFileDirectoryCollection();
+
+ public override void Clear()
+ {
+ ImageFileDirectories.Clear();
+ }
+
+ public override void CopyTo(ObjectModel where)
+ {
+ TIFFObjectModelBase clone = (where as TIFFObjectModelBase);
+ if (clone == null) throw new ObjectModelNotSupportedException();
+
+ for (int i = 0; i < ImageFileDirectories.Count; i++)
+ {
+ clone.ImageFileDirectories.Add(ImageFileDirectories[i].Clone() as TIFFImageFileDirectory);
+ }
+ }
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFPhotometricInterpretation.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFPhotometricInterpretation.cs
new file mode 100644
index 00000000..24abf82b
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFPhotometricInterpretation.cs
@@ -0,0 +1,48 @@
+//
+// TIFFPhotometricInterpretation.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFPhotometricInterpretation
+ {
+ ///
+ /// For bilevel and grayscale images: 0 is imaged as white. The maximum value is imaged as black. This is the normal value for Compression = 2.
+ ///
+ WhiteIsZero = 0x00,
+ ///
+ /// For bilevel and grayscale images: 0 is imaged as black. The maximum value is imaged as white. If this value is specified for Compression = 2, the
+ /// image should display and print reversed.
+ ///
+ BlackIsZero = 0x01,
+ ///
+ /// Indicates a full-color image. Each component is 8 bits deep in a Baseline TIFF RGB image.
+ ///
+ RGB = 0x02,
+ ///
+ /// Indicates a Palette-color image.
+ ///
+ PaletteColor = 0x03,
+ TransparencyMask = 0x04,
+ CMYK = 0x05,
+ YCbCr = 0x06,
+ CIELab = 0x08
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFResolutionUnit.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFResolutionUnit.cs
new file mode 100644
index 00000000..1e6030c6
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFResolutionUnit.cs
@@ -0,0 +1,33 @@
+//
+// TIFFResolutionUnit.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFResolutionUnit : ushort
+ {
+ ///
+ /// No absolute unit of measurement. Used for images that may have a non-square aspect ratio but no meaningful absolute dimensions.
+ ///
+ None = 0x01,
+ Inch = 0x02,
+ Centimeter = 0x03
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFTag.cs b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFTag.cs
new file mode 100644
index 00000000..564b3e29
--- /dev/null
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/DataFormats/Multimedia/Picture/TIFF/TIFFTag.cs
@@ -0,0 +1,186 @@
+//
+// TIFFTag.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2020 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+namespace UniversalEditor.DataFormats.Multimedia.Picture.TIFF
+{
+ public enum TIFFTag
+ {
+ ///
+ /// SHORT or LONG. The number of columns in the image, i.e., the number of pixels per scanline.
+ ///
+ ImageWidth = 256,
+ ///
+ /// SHORT or LONG. The number of rows (sometimes described as scanlines) in the image.
+ ///
+ ImageLength = 257,
+ ///
+ /// SHORT. The number of bits per component. Allowable values for Baseline TIFF grayscale images are 4 and 8, allowing either 16 or 256 distinct shades of gray.
+ ///
+ BitsPerSample = 258,
+ ///
+ /// SHORT. See .
+ ///
+ Compression = 259,
+ ///
+ /// SHORT. See .
+ ///
+ PhotometricInterpretation = 262,
+
+ ///
+ /// SHORT. The width of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file.
+ ///
+ CellWidth = 264,
+ ///
+ /// SHORT. The length of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file. This field should only be present if
+ /// = 2.
+ ///
+ CellLength = 265,
+
+ ///
+ /// SHORT. The logical order of bits within a byte. See . Default is .
+ ///
+ FillOrder = 266,
+
+ ///
+ /// ASCII. The name of the document from which this image was scanned.
+ ///
+ ///
+ DocumentName = 269,
+
+ ///
+ /// SHORT or LONG. For each strip, the byte offset of that strip.
+ ///
+ StripOffsets = 273,
+ ///
+ /// SHORT. The orientation of the image with respect to the rows and columns.
+ ///
+ Orientation = 274,
+ ///
+ /// SHORT. The number of components per pixel. This number is 3 for RGB images, unless extra samples are present. See the ExtraSamples field for further information.
+ ///
+ SamplesPerPixel = 277,
+ ///
+ /// SHORT or LONG. The number of rows in each strip (except possibly the last strip.) For example, if ImageLength is 24, and RowsPerStrip is 10, then there are 3
+ /// strips, with 10 rows in the first strip, 10 rows in the second strip, and 4 rows in the third strip. (The data in the last strip is not padded with 6 extra rows
+ /// of dummy data.)
+ ///
+ RowsPerStrip = 278,
+ ///
+ /// SHORT or LONG. For each strip, the number of bytes in that strip AFTER ANY COMPRESSION.
+ ///
+ StripByteCounts = 279,
+ ///
+ /// RATIONAL. The number of pixels per ResolutionUnit in the ImageWidth (typically, horizontal - see Orientation) direction.
+ ///
+ XResolution = 282,
+ ///
+ /// RATIONAL. The number of pixels per ResolutionUnit in the ImageLength (typically, vertical) direction.
+ ///
+ YResolution = 283,
+ ///
+ /// SHORT. How the components of each pixel are stored.
+ ///
+ PlanarConfiguration = 284,
+ ///
+ /// ASCII. The name of the page from which this image was scanned.
+ ///
+ ///
+ PageName = 285,
+
+ ///
+ /// RATIONAL. X position of the image, i.e. the X offset in ResolutionUnits of the left side of the image, with respect to the left side of the page.
+ ///
+ ///
+ XPosition = 286,
+ ///
+ /// RATIONAL. Y position of the image, i.e. the Y offset in ResolutionUnits of the top of the image, with respect to the top of the page. In the TIFF coordinate scheme,
+ /// the positive Y direction is down, so that is always positive.
+ ///
+ ///
+ YPosition = 287,
+
+ ///
+ /// SHORT. See . Default = 2 ().
+ ///
+ ResolutionUnit = 296,
+ ///
+ /// SHORT. The page number of the page from which this image was scanned. This field is used to specify page numbers of a multiple page (e.g. facsimile) document.
+ /// PageNumber[0] is the page number; PageNumber[1] is the total number of pages in the document. If PageNumber[1] is 0, the total number of pages in the document is
+ /// not available. Pages need not appear in numerical order. The first page is numbered 0 (zero).
+ ///
+ PageNumber = 297,
+
+ ///
+ /// ASCII. Name and version number of the software package(s) used to create the image.
+ ///
+ ///
+ ///
+ Software = 305,
+ ///
+ /// ASCII. Date and time of image creation. The format is: “YYYY:MM:DD HH:MM:SS”, with hours like those on a 24-hour clock, and one space character between the date and
+ /// the time. The length of the string, including the terminating NUL, is 20 bytes.
+ ///
+ DateTime = 306,
+
+ ///
+ /// SHORT. Description of extra components. Specifies that each pixel has m extra components whose interpretation is defined by one of the values listed below.
+ /// When this field is used, the SamplesPerPixel field has a value greater than the PhotometricInterpretation field suggests. For example, full-color RGB data normally
+ /// has SamplesPerPixel = 3. If SamplesPerPixel is greater than 3, then the ExtraSamples field describes the meaning of the extra samples. If SamplesPerPixel is, say, 5
+ /// then ExtraSamples will contain 2 values, one for each extra sample. ExtraSamples is typically used to include non-color information, such as opacity, in an image.
+ /// The possible values for each item in the field's value are defined by . By convention, extra components that are present must be
+ /// stored as the "last components" in each pixel. For example, if SamplesPerPixel is 4 and there is 1 extra component, then it is located in the last component location
+ /// (SamplesPerPixel-1) in each pixel. Components designated as "extra" are just like other components in a pixel. In particular, the size of such components is defined
+ /// by the value of the field. With the introduction of this field, TIFF readers must not assume a particular
+ /// value based on the value of the field. For example, if the file is an RGB file, may be
+ /// greater than 3. The default is no extra samples. This field must be present if there are extra samples.
+ ///
+ ///
+ ///
+ ExtraSamples = 338,
+ ///
+ /// SHORT[SamplesPerPixel]. This field specifies how to interpret each data sample in a pixel. Possible values are defined in .
+ ///
+ SampleFormat = 339,
+
+ ///
+ /// ASCII. Person who created the image.
+ ///
+ Artist = 315,
+
+ ///
+ /// SHORT. This field defines a Red-Green-Blue color map (often called a lookup table) for palette color images. In a palette-color image, a pixel value is used to index
+ /// into an RGB-lookup table. For example, a palette-color pixel having a value of 0 would be displayed according to the 0th Red, Green, Blue triplet. In a TIFF ColorMap,
+ /// all the Red values come first, followed by the Green values, then the Blue values. In the ColorMap, black is represented by 0,0,0 and white is represented by 65535,
+ /// 65535, 65535.
+ ///
+ ColorMap = 320,
+
+ XMPMetadata = 700,
+
+ ///
+ /// ASCII. Copyright notice of the person or organization that claims the copyright to the image. The complete copyright statement should be listed in this field
+ /// including any dates and statements of claims. For example, "Copyright, John Smith, 19xx. All rights reserved."
+ ///
+ Copyright = 33432,
+
+ ICCProfile = 34675
+ }
+}
diff --git a/Plugins/UniversalEditor.Plugins.Multimedia/UniversalEditor.Plugins.Multimedia.csproj b/Plugins/UniversalEditor.Plugins.Multimedia/UniversalEditor.Plugins.Multimedia.csproj
index 86ddce75..ae27e3d3 100644
--- a/Plugins/UniversalEditor.Plugins.Multimedia/UniversalEditor.Plugins.Multimedia.csproj
+++ b/Plugins/UniversalEditor.Plugins.Multimedia/UniversalEditor.Plugins.Multimedia.csproj
@@ -325,6 +325,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -377,6 +389,7 @@
+
\ No newline at end of file