Browse Source

Add initial Tiff codec projects, and TiffGen

pull/1570/head
Andrew Wilkinson 9 years ago
parent
commit
1b568ceedd
  1. 7
      src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj
  2. 205
      src/ImageSharp.Formats.Tiff/TiffTags.cs
  3. 27
      src/ImageSharp.Formats.Tiff/TiffType.cs
  4. 18
      tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj
  5. 28
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs
  6. 41
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs
  7. 18
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs
  8. 31
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs
  9. 25
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs
  10. 102
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs
  11. 47
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs
  12. 42
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs
  13. 91
      tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs

7
src/ImageSharp.Formats.Tiff/ImageSharp.Formats.Tiff.csproj

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
</PropertyGroup>
</Project>

205
src/ImageSharp.Formats.Tiff/TiffTags.cs

@ -0,0 +1,205 @@
// <copyright file="TiffTags.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Constants representing tag IDs in the Tiff file-format.
/// </summary>
public class TiffTags
{
// Section 8: Baseline Fields
public const int Artist = 315;
public const int BitsPerSample = 258;
public const int CellLength = 265;
public const int CellWidth = 264;
public const int ColorMap = 320;
public const int Compression = 259;
public const int Copyright = 33432;
public const int DateTime = 306;
public const int ExtraSamples = 338;
public const int FillOrder = 266;
public const int FreeByteCounts = 289;
public const int FreeOffsets = 288;
public const int GrayResponseCurve = 291;
public const int GrayResponseUnit = 290;
public const int HostComputer = 316;
public const int ImageDescription = 270;
public const int ImageLength = 257;
public const int ImageWidth = 256;
public const int Make = 271;
public const int MaxSampleValue = 281;
public const int MinSampleValue = 280;
public const int Model = 272;
public const int NewSubfileType = 254;
public const int Orientation = 274;
public const int PhotometricInterpretation = 262;
public const int PlanarConfiguration = 284;
public const int ResolutionUnit = 296;
public const int RowsPerStrip = 278;
public const int SamplesPerPixel = 277;
public const int Software = 305;
public const int StripByteCounts = 279;
public const int StripOffsets = 273;
public const int SubfileType = 255;
public const int Threshholding = 263;
public const int XResolution = 282;
public const int YResolution = 283;
// Section 11: CCITT Bilevel Encodings
public const int T4Options = 292;
public const int T6Options = 293;
// Section 12: Document Storage and Retrieval
public const int DocumentName = 269;
public const int PageName = 285;
public const int PageNumber = 297;
public const int XPosition = 286;
public const int YPosition = 287;
// Section 14: Differencing Predictor
public const int Predictor = 317;
// Section 15: Tiled Images
public const int TileWidth = 322;
public const int TileLength = 323;
public const int TileOffsets = 324;
public const int TileByteCounts = 325;
// Section 16: CMYK Images
public const int InkSet = 332;
public const int NumberOfInks = 334;
public const int InkNames = 333;
public const int DotRange = 336;
public const int TargetPrinter = 337;
// Section 17: Halftone Hints
public const int HalftoneHints = 321;
// Section 19: Data Sample Format
public const int SampleFormat = 339;
public const int SMinSampleValue = 340;
public const int SMaxSampleValue = 341;
// Section 20: RGB Image Colorimetry
public const int WhitePoint = 318;
public const int PrimaryChromaticities = 319;
public const int TransferFunction = 301;
public const int TransferRange = 342;
public const int ReferenceBlackWhite = 532;
// Section 21: YCbCr Images
public const int YCbCrCoefficients = 529;
public const int YCbCrSubSampling = 530;
public const int YCbCrPositioning = 531;
// Section 22: JPEG Compression
public const int JpegProc = 512;
public const int JpegInterchangeFormat = 513;
public const int JpegInterchangeFormatLength = 514;
public const int JpegRestartInterval = 515;
public const int JpegLosslessPredictors = 517;
public const int JpegPointTransforms = 518;
public const int JpegQTables = 519;
public const int JpegDCTables = 520;
public const int JpegACTables = 521;
// TIFF Supplement 1: Adobe Pagemaker 6.0
public const int SubIFDs = 330;
public const int ClipPath = 343;
public const int XClipPathUnits = 344;
public const int YClipPathUnits = 345;
public const int Indexed = 346;
public const int ImageID = 32781;
public const int OpiProxy = 351;
// TIFF Supplement 2: Adobe Photoshop
public const int ImageSourceData = 37724;
// TIFF/EP Specification: Additional Tags
public const int JPEGTables = 0x015B;
public const int CFARepeatPatternDim = 0x828D;
public const int BatteryLevel = 0x828F;
public const int Interlace = 0x8829;
public const int TimeZoneOffset = 0x882A;
public const int SelfTimerMode = 0x882B;
public const int Noise = 0x920D;
public const int ImageNumber = 0x9211;
public const int SecurityClassification = 0x9212;
public const int ImageHistory = 0x9213;
public const int TiffEPStandardID = 0x9216;
// TIFF-F/FX Specification (http://www.ietf.org/rfc/rfc2301.txt)
public const int BadFaxLines = 326;
public const int CleanFaxData = 327;
public const int ConsecutiveBadFaxLines = 328;
public const int GlobalParametersIFD = 400;
public const int ProfileType = 401;
public const int FaxProfile = 402;
public const int CodingMethod = 403;
public const int VersionYear = 404;
public const int ModeNumber = 405;
public const int Decode = 433;
public const int DefaultImageColor = 434;
public const int StripRowCounts = 559;
public const int ImageLayer = 34732;
// Embedded Metadata
public const int Xmp = 700;
public const int Iptc = 33723;
public const int Photoshop = 34377;
public const int ExifIFD = 34665;
public const int GpsIFD = 34853;
public const int InteroperabilityIFD = 40965;
// Other Private TIFF tags (http://www.awaresystems.be/imaging/tiff/tifftags/private.html)
public const int WangAnnotation = 32932;
public const int MDFileTag = 33445;
public const int MDScalePixel = 33446;
public const int MDColorTable = 33447;
public const int MDLabName = 33448;
public const int MDSampleInfo = 33449;
public const int MDPrepDate = 33450;
public const int MDPrepTime = 33451;
public const int MDFileUnits = 33452;
public const int ModelPixelScaleTag = 33550;
public const int IngrPacketDataTag = 33918;
public const int IngrFlagRegisters = 33919;
public const int IrasBTransformationMatrix = 33920;
public const int ModelTiePointTag = 33922;
public const int ModelTransformationTag = 34264;
public const int IccProfile = 34675;
public const int GeoKeyDirectoryTag = 34735;
public const int GeoDoubleParamsTag = 34736;
public const int GeoAsciiParamsTag = 34737;
public const int HylaFAXFaxRecvParams = 34908;
public const int HylaFAXFaxSubAddress = 34909;
public const int HylaFAXFaxRecvTime = 34910;
public const int GdalMetadata = 42112;
public const int GdalNodata = 42113;
public const int OceScanjobDescription = 50215;
public const int OceApplicationSelector = 50216;
public const int OceIdentificationNumber = 50217;
public const int OceImageLogicCharacteristics = 50218;
public const int AliasLayerMetadata = 50784;
}
}

27
src/ImageSharp.Formats.Tiff/TiffType.cs

@ -0,0 +1,27 @@
// <copyright file="TiffType.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
/// <summary>
/// Enumeration representing the data types understood by the Tiff file-format.
/// </summary>
public enum TiffType
{
Byte = 1,
Ascii = 2,
Short = 3,
Long = 4,
Rational = 5,
SByte = 6,
Undefined = 7,
SShort = 8,
SLong = 9,
SRational = 10,
Float = 11,
Double = 12,
Ifd = 13
}
}

18
tests/ImageSharp.Formats.Tiff.Tests/ImageSharp.Formats.Tiff.Tests.csproj

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp.Formats.Tiff\ImageSharp.Formats.Tiff.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170123-02" />
<PackageReference Include="xunit" Version="2.2.0-beta5-build3474" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-beta5-build1225" />
</ItemGroup>
</Project>

28
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteArrayUtility.cs

@ -0,0 +1,28 @@
// <copyright file="ByteArrayUtility.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
public static class ByteArrayUtility
{
public static byte[] WithByteOrder(this byte[] bytes, bool isLittleEndian)
{
if (BitConverter.IsLittleEndian != isLittleEndian)
{
byte[] reversedBytes = new byte[bytes.Length];
Array.Copy(bytes, reversedBytes, bytes.Length);
Array.Reverse(reversedBytes);
return reversedBytes;
}
else
{
return bytes;
}
}
}
}

41
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/ByteBuffer.cs

@ -0,0 +1,41 @@
// <copyright file="ByteBuffer.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
public class ByteBuffer
{
List<byte> bytes = new List<byte>();
bool isLittleEndian;
public ByteBuffer(bool isLittleEndian)
{
this.isLittleEndian = isLittleEndian;
}
public void AddByte(byte value)
{
bytes.Add(value);
}
public void AddUInt16(ushort value)
{
bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(isLittleEndian));
}
public void AddUInt32(uint value)
{
bytes.AddRange(BitConverter.GetBytes(value).WithByteOrder(isLittleEndian));
}
public byte[] ToArray()
{
return bytes.ToArray();
}
}
}

18
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/ITiffGenDataSource.cs

@ -0,0 +1,18 @@
// <copyright file="ITiffGenDataSource.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
/// <summary>
/// An interface for any class within the Tiff generator that produces data to be included in the file.
/// </summary>
public interface ITiffGenDataSource
{
IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian);
}
}

31
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataBlock.cs

@ -0,0 +1,31 @@
// <copyright file="TiffGenDataBlock.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
/// <summary>
/// A utility data structure to represent an independent block of data in a Tiff file.
/// These may be located in any order within a Tiff file.
/// </summary>
public class TiffGenDataBlock
{
public TiffGenDataBlock(byte[] bytes)
{
this.Bytes = bytes;
this.References = new List<TiffGenDataReference>();
}
public byte[] Bytes { get; }
public IList<TiffGenDataReference> References { get; }
public void AddReference(byte[] bytes, int offset)
{
References.Add(new TiffGenDataReference(bytes, offset));
}
}
}

25
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenDataReference.cs

@ -0,0 +1,25 @@
// <copyright file="TiffGenDataReference.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
/// <summary>
/// A utility data structure to represent a reference from one block of data to another in a Tiff file.
/// </summary>
public class TiffGenDataReference
{
public TiffGenDataReference(byte[] bytes, int offset)
{
this.Bytes = bytes;
this.Offset = offset;
}
public byte[] Bytes { get; }
public int Offset { get; }
}
}

102
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenEntry.cs

@ -0,0 +1,102 @@
// <copyright file="TiffGenEntry.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ImageSharp.Formats;
/// <summary>
/// A utility data structure to represent Tiff IFD entries in unit tests.
/// </summary>
public abstract class TiffGenEntry : ITiffGenDataSource
{
private TiffGenEntry(ushort tag, TiffType type)
{
this.Tag = tag;
this.Type = type;
}
public ushort Tag { get; }
public TiffType Type { get; }
public abstract IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian);
public static TiffGenEntry Ascii(ushort tag, string value)
{
return new TiffGenEntryAscii(tag, value);
}
public static TiffGenEntry Integer(ushort tag, TiffType type, int value)
{
return TiffGenEntry.Integer(tag, type, new int[] {value});
}
public static TiffGenEntry Integer(ushort tag, TiffType type, int[] value)
{
if (type != TiffType.Byte && type != TiffType.Short && type != TiffType.Long &&
type != TiffType.SByte && type != TiffType.SShort && type != TiffType.SLong)
throw new ArgumentException(nameof(type), "The specified type is not an integer type.");
return new TiffGenEntryInteger(tag, type, value);
}
private class TiffGenEntryAscii : TiffGenEntry
{
public TiffGenEntryAscii(ushort tag, string value) : base(tag, TiffType.Ascii)
{
this.Value = value;
}
public string Value { get; }
public override IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian)
{
byte[] bytes = Encoding.ASCII.GetBytes($"{Value}\0");
return new[] { new TiffGenDataBlock(bytes) };
}
}
private class TiffGenEntryInteger : TiffGenEntry
{
public TiffGenEntryInteger(ushort tag, TiffType type, int[] value) : base(tag, type)
{
this.Value = value;
}
public int[] Value { get; }
public override IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian)
{
byte[] bytes = GetBytes().SelectMany(b => b.WithByteOrder(isLittleEndian)).ToArray();
return new[] { new TiffGenDataBlock(bytes) };
}
private IEnumerable<byte[]> GetBytes()
{
switch (Type)
{
case TiffType.Byte:
return Value.Select(i => new byte[] { (byte)i });
case TiffType.Short:
return Value.Select(i => BitConverter.GetBytes((ushort)i));
case TiffType.Long:
return Value.Select(i => BitConverter.GetBytes((uint)i));
case TiffType.SByte:
return Value.Select(i => BitConverter.GetBytes((sbyte)i));
case TiffType.SShort:
return Value.Select(i => BitConverter.GetBytes((short)i));
case TiffType.SLong:
return Value.Select(i => BitConverter.GetBytes((int)i));
default:
throw new InvalidOperationException();
}
}
}
}
}

47
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenExtensions.cs

@ -0,0 +1,47 @@
// <copyright file="TiffGenExtensions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.IO;
using System.Linq;
/// <summary>
/// A utility class for generating in-memory Tiff files for use in unit tests.
/// </summary>
public static class TiffGenExtensions
{
public static byte[] ToBytes(this ITiffGenDataSource dataSource, bool isLittleEndian)
{
var dataBlocks = dataSource.GetData(isLittleEndian);
int offset = 0;
foreach (var dataBlock in dataBlocks)
{
byte[] offsetBytes = BitConverter.GetBytes(offset).WithByteOrder(isLittleEndian);
foreach (var reference in dataBlock.References)
{
reference.Bytes[reference.Offset + 0] = offsetBytes[0];
reference.Bytes[reference.Offset + 1] = offsetBytes[1];
reference.Bytes[reference.Offset + 2] = offsetBytes[2];
reference.Bytes[reference.Offset + 3] = offsetBytes[3];
}
offset += dataBlock.Bytes.Length;
}
return dataBlocks.SelectMany(b => b.Bytes).ToArray();
}
public static Stream ToStream(this ITiffGenDataSource dataSource, bool isLittleEndian)
{
var bytes = dataSource.ToBytes(isLittleEndian);
return new MemoryStream(bytes);
}
}
}

42
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenHeader.cs

@ -0,0 +1,42 @@
// <copyright file="TiffGenHeader.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// A utility data structure to represent a Tiff file-header.
/// </summary>
public class TiffGenHeader : ITiffGenDataSource
{
public TiffGenHeader()
{
this.MagicNumber = 42;
}
public ushort? ByteOrderMarker { get; set; }
public ushort MagicNumber { get; set; }
public TiffGenIfd FirstIfd { get; set; }
public IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian)
{
ByteBuffer bytes = new ByteBuffer(isLittleEndian);
bytes.AddUInt16(ByteOrderMarker ?? (isLittleEndian ? (ushort)0x4949 : (ushort)0x4D4D));
bytes.AddUInt16(MagicNumber);
bytes.AddUInt32(0);
var headerData = new TiffGenDataBlock(bytes.ToArray());
var firstIfdData = FirstIfd.GetData(isLittleEndian);
firstIfdData.First().AddReference(headerData.Bytes, 4);
return new [] { headerData }.Concat(firstIfdData);
}
}
}

91
tests/ImageSharp.Formats.Tiff.Tests/TestUtilities/Tiff/TiffGenIfd.cs

@ -0,0 +1,91 @@
// <copyright file="TiffGenIfd.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// A utility data structure to represent Tiff IFDs in unit tests.
/// </summary>
public class TiffGenIfd : ITiffGenDataSource
{
public TiffGenIfd()
{
this.Entries = new List<TiffGenEntry>();
}
public List<TiffGenEntry> Entries { get; }
public TiffGenIfd NextIfd { get; set; }
public IEnumerable<TiffGenDataBlock> GetData(bool isLittleEndian)
{
ByteBuffer bytes = new ByteBuffer(isLittleEndian);
List<TiffGenDataBlock> dataBlocks = new List<TiffGenDataBlock>();
List<Tuple<TiffGenDataBlock, int>> entryReferences = new List<Tuple<TiffGenDataBlock, int>>();
// Add the entry count
bytes.AddUInt16((ushort)Entries.Count);
// Add all IFD entries
int entryOffset = 2;
foreach (var entry in Entries)
{
var entryData = entry.GetData(isLittleEndian);
var entryBytes = entryData.First().Bytes;
var entryCount = entryBytes.Length;
bytes.AddUInt16(entry.Tag);
bytes.AddUInt16((ushort)entry.Type);
bytes.AddUInt32((uint)entryCount);
if (entryCount <=4)
{
bytes.AddByte(entryCount > 0 ? entryBytes[0] : (byte)0);
bytes.AddByte(entryCount > 1 ? entryBytes[1] : (byte)0);
bytes.AddByte(entryCount > 2 ? entryBytes[2] : (byte)0);
bytes.AddByte(entryCount > 3 ? entryBytes[3] : (byte)0);
dataBlocks.AddRange(entryData.Skip(1));
}
else
{
bytes.AddUInt32(0);
dataBlocks.AddRange(entryData);
entryReferences.Add(Tuple.Create(entryData.First(), entryOffset + 8));
}
entryOffset += 12;
}
// Add reference to next IFD
bytes.AddUInt32(0);
// Build the data
var ifdData = new TiffGenDataBlock(bytes.ToArray());
foreach (var entryReference in entryReferences)
{
entryReference.Item1.AddReference(ifdData.Bytes, entryReference.Item2);
}
IEnumerable<TiffGenDataBlock> nextIfdData = new TiffGenDataBlock[0];
if (NextIfd != null)
{
nextIfdData = NextIfd.GetData(isLittleEndian);
nextIfdData.First().AddReference(ifdData.Bytes, ifdData.Bytes.Length - 4);
}
return new [] { ifdData }.Concat(dataBlocks).Concat(nextIfdData);
}
}
}
Loading…
Cancel
Save