├── EXRViewer ├── purplemonkey.exr ├── purplemonkey-rle.exr ├── purplemonkey-zip.exr ├── App.config ├── Properties │ └── AssemblyInfo.cs ├── Program.cs ├── CommandLineArguments.cs └── EXRViewer.csproj ├── SharpEXR ├── App.config ├── RoundingMode.cs ├── EnvMap.cs ├── GammaEncoding.cs ├── PixelType.cs ├── LevelMode.cs ├── LineOrder.cs ├── PartType.cs ├── ImageSourceFormat.cs ├── EXRCompression.cs ├── AttributeTypes │ ├── TimeCode.cs │ ├── V2I.cs │ ├── V2F.cs │ ├── Rational.cs │ ├── M33F.cs │ ├── V3I.cs │ ├── V3F.cs │ ├── Box2I.cs │ ├── Box2F.cs │ ├── KeyCode.cs │ ├── Chromaticities.cs │ ├── TileDesc.cs │ └── M44F.cs ├── ChannelConfiguration.cs ├── EXRFormatException.cs ├── ImageDestFormat.cs ├── OffsetTable.cs ├── Channel.cs ├── Properties │ └── AssemblyInfo.cs ├── ChannelList.cs ├── Compression │ └── RLE.cs ├── EXRVersion.cs ├── EXRReader.cs ├── EXRHeader.cs ├── SharpEXR.csproj ├── ColorSpace │ ├── Gamma.cs │ └── XYZ.cs ├── EXRFile.cs ├── HalfHelper.cs ├── EXRAttribute.cs ├── EXRPart.cs └── Half.cs ├── LICENSE ├── SharpEXR.sln ├── README.md └── .gitignore /EXRViewer/purplemonkey.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Enichan/sharpexr/HEAD/EXRViewer/purplemonkey.exr -------------------------------------------------------------------------------- /EXRViewer/purplemonkey-rle.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Enichan/sharpexr/HEAD/EXRViewer/purplemonkey-rle.exr -------------------------------------------------------------------------------- /EXRViewer/purplemonkey-zip.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Enichan/sharpexr/HEAD/EXRViewer/purplemonkey-zip.exr -------------------------------------------------------------------------------- /SharpEXR/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EXRViewer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SharpEXR/RoundingMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum RoundingMode { 9 | Down = 0, 10 | Up = 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SharpEXR/EnvMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public enum EnvMap { 9 | LatLong = 0, 10 | Cube = 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SharpEXR/GammaEncoding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum GammaEncoding { 9 | Linear, 10 | Gamma, 11 | sRGB 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SharpEXR/PixelType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum PixelType { 9 | UInt = 0, 10 | Half = 1, 11 | Float = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SharpEXR/LevelMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum LevelMode { 9 | One = 0, 10 | Mipmap = 1, 11 | Ripmap = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SharpEXR/LineOrder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum LineOrder { 9 | IncreasingY = 0, 10 | DecreasingY = 1, 11 | RandomY = 2 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SharpEXR/PartType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum PartType { 9 | ScanLine, 10 | Tiled, 11 | DeepScanLine, 12 | DeepTile 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SharpEXR/ImageSourceFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum ImageSourceFormat { 9 | SingleRGBA, 10 | HalfRGBA, 11 | SingleRGB, 12 | HalfRGB 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SharpEXR/EXRCompression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum EXRCompression { 9 | None = 0, 10 | RLE = 1, 11 | ZIPS = 2, 12 | ZIP = 3, 13 | PIZ = 4, 14 | PXR24 = 5, 15 | B44 = 6, 16 | B44A = 7 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/TimeCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct TimeCode { 9 | public readonly uint TimeAndFlags; 10 | public readonly uint UserData; 11 | 12 | public TimeCode(uint timeAndFlags, uint userData) { 13 | TimeAndFlags = timeAndFlags; 14 | UserData = userData; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SharpEXR/ChannelConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum ChannelConfiguration { 9 | /// 10 | /// First byte is blue, then green, then red, then (optionally) alpha 11 | /// 12 | BGR, 13 | /// 14 | /// First byte is red, then green, then blue, then (optionally) alpha 15 | /// 16 | RGB 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SharpEXR/EXRFormatException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public class EXRFormatException : Exception { 9 | public EXRFormatException() 10 | : base() { 11 | } 12 | public EXRFormatException(string message) 13 | : base(message) { 14 | } 15 | public EXRFormatException(string message, Exception innerException) 16 | : base(message, innerException) { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/V2I.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct V2I { 9 | public int V0; 10 | public int V1; 11 | 12 | public V2I(int v0, int v1) { 13 | V0 = v0; 14 | V1 = v1; 15 | } 16 | 17 | public override string ToString() { 18 | return string.Format("{0}: {1}, {2}", GetType().Name, V0, V1); 19 | } 20 | 21 | public int X { get { return V0; } } 22 | public int Y { get { return V1; } } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/V2F.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct V2F { 9 | public float V0; 10 | public float V1; 11 | 12 | public V2F(float v0, float v1) { 13 | V0 = v0; 14 | V1 = v1; 15 | } 16 | 17 | public override string ToString() { 18 | return string.Format("{0}: {1}, {2}", GetType().Name, V0, V1); 19 | } 20 | 21 | public float X { get { return V0; } } 22 | public float Y { get { return V1; } } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SharpEXR/ImageDestFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public enum ImageDestFormat { 9 | RGB8, 10 | RGBA8, 11 | PremultipliedRGBA8, 12 | RGB16, 13 | RGBA16, 14 | PremultipliedRGBA16, 15 | RGB32, 16 | RGBA32, 17 | PremultipliedRGBA32, 18 | BGR8, 19 | BGRA8, 20 | PremultipliedBGRA8, 21 | BGR16, 22 | BGRA16, 23 | PremultipliedBGRA16, 24 | BGR32, 25 | BGRA32, 26 | PremultipliedBGRA32 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/Rational.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct Rational { 9 | public readonly int Numerator; 10 | public readonly uint Denominator; 11 | 12 | public Rational(int numerator, uint denominator) { 13 | Numerator = numerator; 14 | Denominator = denominator; 15 | } 16 | 17 | public override string ToString() { 18 | return string.Format("{0}/{1}", Numerator, Denominator); 19 | } 20 | 21 | public double Value { get { return (double)Numerator / Denominator; } } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/M33F.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct M33F { 9 | public readonly float[] Values; 10 | 11 | public M33F(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8) { 12 | Values = new float[9]; 13 | Values[0] = v0; 14 | Values[1] = v1; 15 | Values[2] = v2; 16 | Values[3] = v3; 17 | Values[4] = v4; 18 | Values[5] = v5; 19 | Values[6] = v6; 20 | Values[7] = v7; 21 | Values[8] = v8; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/V3I.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct V3I { 9 | public int V0; 10 | public int V1; 11 | public int V2; 12 | 13 | public V3I(int v0, int v1, int v2) { 14 | V0 = v0; 15 | V1 = v1; 16 | V2 = v2; 17 | } 18 | 19 | public override string ToString() { 20 | return string.Format("{0}: {1}, {2}, {3}", GetType().Name, V0, V1, V2); 21 | } 22 | 23 | public int X { get { return V0; } } 24 | public int Y { get { return V1; } } 25 | public int Z { get { return V2; } } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/V3F.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct V3F { 9 | public float V0; 10 | public float V1; 11 | public float V2; 12 | 13 | public V3F(float v0, float v1, float v2) { 14 | V0 = v0; 15 | V1 = v1; 16 | V2 = v2; 17 | } 18 | 19 | public override string ToString() { 20 | return string.Format("{0}: {1}, {2}, {3}", GetType().Name, V0, V1, V2); 21 | } 22 | 23 | public float X { get { return V0; } } 24 | public float Y { get { return V1; } } 25 | public float Z { get { return V2; } } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/Box2I.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct Box2I { 9 | public readonly int XMin; 10 | public readonly int YMin; 11 | public readonly int XMax; 12 | public readonly int YMax; 13 | 14 | public Box2I(int xMin, int yMin, int xMax, int yMax) { 15 | XMin = xMin; 16 | YMin = yMin; 17 | XMax = xMax; 18 | YMax = yMax; 19 | } 20 | 21 | public override string ToString() { 22 | return string.Format("{0}: ({1}, {2})-({3}, {4})", GetType().Name, XMin, YMin, XMax, YMax); 23 | } 24 | 25 | public int Width { get { return (XMax - XMin) + 1; } } 26 | public int Height { get { return (YMax - YMin) + 1; } } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/Box2F.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct Box2F { 9 | public readonly float XMin; 10 | public readonly float YMin; 11 | public readonly float XMax; 12 | public readonly float YMax; 13 | 14 | public Box2F(float xMin, float yMin, float xMax, float yMax) { 15 | XMin = xMin; 16 | YMin = yMin; 17 | XMax = xMax; 18 | YMax = yMax; 19 | } 20 | 21 | public override string ToString() { 22 | return string.Format("{0}: ({1}, {2})-({3}, {4})", GetType().Name, XMin, YMin, XMax, YMax); 23 | } 24 | 25 | public float Width { get { return (XMax - XMin) + 1; } } 26 | public float Height { get { return (YMax - YMin) + 1; } } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/KeyCode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct KeyCode { 9 | public readonly int FilmMfcCode; 10 | public readonly int FilmType; 11 | public readonly int Prefix; 12 | public readonly int Count; 13 | public readonly int PerfOffset; 14 | public readonly int PerfsPerFrame; 15 | public readonly int PerfsPerCount; 16 | 17 | public KeyCode(int filmMfcCode, int filmType, int prefix, int count, int perfOffset, int perfsPerFrame, int perfsPerCount) { 18 | FilmMfcCode = filmMfcCode; 19 | FilmType = filmType; 20 | Prefix = prefix; 21 | Count = count; 22 | PerfOffset = perfOffset; 23 | PerfsPerFrame = perfsPerFrame; 24 | PerfsPerCount = perfsPerCount; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/Chromaticities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct Chromaticities { 9 | public readonly float RedX; 10 | public readonly float RedY; 11 | public readonly float GreenX; 12 | public readonly float GreenY; 13 | public readonly float BlueX; 14 | public readonly float BlueY; 15 | public readonly float WhiteX; 16 | public readonly float WhiteY; 17 | 18 | public Chromaticities(float redX, float redY, float greenX, float greenY, float blueX, float blueY, float whiteX, float whiteY) { 19 | RedX = redX; 20 | RedY = redY; 21 | GreenX = greenX; 22 | GreenY = greenY; 23 | BlueX = blueX; 24 | BlueY = blueY; 25 | WhiteX = whiteX; 26 | WhiteY = whiteY; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/TileDesc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct TileDesc { 9 | public readonly uint XSize; 10 | public readonly uint YSize; 11 | public readonly LevelMode LevelMode; 12 | public readonly RoundingMode RoundingMode; 13 | 14 | public TileDesc(uint xSize, uint ySize, byte mode) { 15 | XSize = xSize; 16 | YSize = ySize; 17 | 18 | // mode is levelMode + roundingMode * 16 19 | var roundingMode = (mode & 0xF0) >> 4; 20 | var levelMode = mode & 0xF; 21 | 22 | RoundingMode = (SharpEXR.RoundingMode)roundingMode; 23 | LevelMode = (SharpEXR.LevelMode)levelMode; 24 | } 25 | 26 | public override string ToString() { 27 | return string.Format("{0}: XSize={1}, YSize={2}", GetType().Name, XSize, YSize); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SharpEXR/AttributeTypes/M44F.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.AttributeTypes { 8 | public struct M44F { 9 | public readonly float[] Values; 10 | 11 | public M44F(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, 12 | float v8, float v9, float v10, float v11, float v12, float v13, float v14, float v15) { 13 | Values = new float[9]; 14 | Values[0] = v0; 15 | Values[1] = v1; 16 | Values[2] = v2; 17 | Values[3] = v3; 18 | Values[4] = v4; 19 | Values[5] = v5; 20 | Values[6] = v6; 21 | Values[7] = v7; 22 | Values[8] = v8; 23 | Values[9] = v9; 24 | Values[10] = v10; 25 | Values[11] = v11; 26 | Values[12] = v12; 27 | Values[13] = v13; 28 | Values[14] = v14; 29 | Values[15] = v15; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SharpEXR/OffsetTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public class OffsetTable : IEnumerable { 9 | public List Offsets { get; set; } 10 | 11 | public OffsetTable() { 12 | Offsets = new List(); 13 | } 14 | 15 | public OffsetTable(int capacity) { 16 | Offsets = new List(capacity); 17 | } 18 | 19 | public void Read(IEXRReader reader, int count) { 20 | for (int i = 0; i < count; i++) { 21 | Offsets.Add(reader.ReadUInt32()); 22 | reader.ReadUInt32(); // skip 4 bytes because we're using uints not ulongs 23 | } 24 | } 25 | 26 | public IEnumerator GetEnumerator() { 27 | return Offsets.GetEnumerator(); 28 | } 29 | 30 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 31 | return GetEnumerator(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Emma Maassen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /SharpEXR/Channel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public class Channel { 9 | public string Name { get; set; } 10 | public PixelType Type { get; set; } 11 | public bool Linear { get; set; } 12 | public int XSampling { get; set; } 13 | public int YSampling { get; set; } 14 | 15 | public byte[] Reserved { get; set; } 16 | 17 | public Channel(string name, PixelType type, bool linear, int xSampling, int ySampling) 18 | : this(name, type, linear, 0, 0, 0, xSampling, ySampling) { 19 | } 20 | 21 | public Channel(string name, PixelType type, bool linear, byte reserved0, byte reserved1, byte reserved2, int xSampling, int ySampling) { 22 | Name = name; 23 | Type = type; 24 | Linear = linear; 25 | Reserved = new byte[3] { reserved0, reserved1, reserved2 }; 26 | } 27 | 28 | public override string ToString() { 29 | return string.Format("{0} {1} {2}", GetType().Name, Name, Type); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SharpEXR.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpEXR", "SharpEXR\SharpEXR.csproj", "{EFFACB6C-98D5-4B92-839B-30E9EB51531D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EXRViewer", "EXRViewer\EXRViewer.csproj", "{26AB652C-EE76-4248-98BD-D125E258A544}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {EFFACB6C-98D5-4B92-839B-30E9EB51531D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {EFFACB6C-98D5-4B92-839B-30E9EB51531D}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {EFFACB6C-98D5-4B92-839B-30E9EB51531D}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {EFFACB6C-98D5-4B92-839B-30E9EB51531D}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {26AB652C-EE76-4248-98BD-D125E258A544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {26AB652C-EE76-4248-98BD-D125E258A544}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {26AB652C-EE76-4248-98BD-D125E258A544}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {26AB652C-EE76-4248-98BD-D125E258A544}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /EXRViewer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EXRViewer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EXRViewer")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4c612ec7-33c7-439b-83ac-2c55778551aa")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SharpEXR/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SharpEXR")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SharpEXR")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("94318db7-c70e-4f92-84eb-00e6848305c0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EXRViewer/Program.cs: -------------------------------------------------------------------------------- 1 | using SharpEXR; 2 | using SharpEXR.ColorSpace; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Drawing; 6 | using System.Drawing.Imaging; 7 | using System.Linq; 8 | using System.Runtime.InteropServices; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | 13 | namespace EXRViewer { 14 | class Program { 15 | static void Main() { 16 | var args = CommandLineArguments.Parse(); 17 | 18 | var img = EXRFile.FromFile(args[0]); 19 | var part = img.Parts[0]; 20 | //part.OpenParallel(() => { return new EXRReader(new System.IO.BinaryReader(System.IO.File.OpenRead(args[0]))); }); 21 | part.OpenParallel(args[0]); 22 | 23 | var bmp = new Bitmap(part.DataWindow.Width, part.DataWindow.Height); 24 | var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); 25 | var destBytes = part.GetBytes(ImageDestFormat.BGRA8, GammaEncoding.sRGB, data.Stride); 26 | Marshal.Copy(destBytes, 0, data.Scan0, destBytes.Length); 27 | bmp.UnlockBits(data); 28 | 29 | part.Close(); 30 | 31 | var frm = new Form(); 32 | frm.BackgroundImage = bmp; 33 | frm.FormBorderStyle = FormBorderStyle.FixedDialog; 34 | frm.MaximizeBox = false; 35 | frm.ClientSize = new System.Drawing.Size(bmp.Width, bmp.Height); 36 | frm.ShowDialog(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SharpEXR 2 | 3 | C# implementation of OpenEXR file format, a high dynamic range imaging image file format created by Industrial Light and Magic (ILM). 4 | 5 | Currently only reads single part files with uncompressed scanlines. 6 | 7 | ## Example Usage 8 | 9 | ```cs 10 | var filePath = "somefile.exr"; 11 | 12 | // load the EXR file headers and parts, but not actual pixel data 13 | var exrFile = EXRFile.FromFile(filePath); 14 | var part = exrFile.Parts[0]; 15 | 16 | // read pixel data for first part in file 17 | // synchronously, slower 18 | //part.Open(filePath); 19 | 20 | // open part while reading pixel data in parallel 21 | part.OpenParallel(filePath); 22 | 23 | // get pixel data as an array of RGBA unsigned bytes, 24 | // auto detecting source format, 25 | // converted to sRGB (screen) color space, 26 | // premultiplying RGB values by alpha values 27 | byte[] bytes = part.GetBytes(ImageDestFormat.PremultipliedBGRA8, GammaEncoding.sRGB); 28 | 29 | // get pixel data as an array of BGR 16 bit floats, 30 | // auto detecting source format, 31 | // converted to Gamma color space, 32 | // not including alpha channel 33 | Half[] halfs = part.GetHalfs(ChannelConfiguration.BGR, false, GammaEncoding.Gamma, false); 34 | 35 | // get pixel data as an array of RGBA 32 bit floats, 36 | // auto detecting source format, 37 | // not converted (linear color space), 38 | // including alpha but not premultiplied 39 | float[] floats = part.GetFloats(ChannelConfiguration.RGB, true, GammaEncoding.Linear, true); 40 | 41 | // close part 42 | part.Close(); 43 | ``` 44 | 45 | ## Saving 46 | 47 | Saving files is currently not supported. 48 | 49 | ## Single-threaded Load 50 | 51 | The project is compiled with the PARALLEL conditional set by default. Removing this conditional compilation symbol will force the library to read image scanlines sequentially instead of in parallel. This is much slower and not recommended. -------------------------------------------------------------------------------- /SharpEXR/ChannelList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | public class ChannelList : IEnumerable { 9 | public List Channels { get; set; } 10 | 11 | public ChannelList() { 12 | Channels = new List(); 13 | } 14 | 15 | public void Read(EXRFile file, IEXRReader reader, int size) { 16 | var totalSize = 0; 17 | Channel channel; 18 | int bytesRead; 19 | 20 | while (ReadChannel(file, reader, out channel, out bytesRead)) { 21 | Channels.Add(channel); 22 | totalSize += bytesRead; 23 | 24 | if (totalSize > size) { 25 | throw new EXRFormatException("Read " + totalSize + " bytes but Size was " + size + "."); 26 | } 27 | } 28 | totalSize += bytesRead; 29 | 30 | if (totalSize != size) { 31 | throw new EXRFormatException("Read " + totalSize + " bytes but Size was " + size + "."); 32 | } 33 | } 34 | 35 | private bool ReadChannel(EXRFile file, IEXRReader reader, out Channel channel, out int bytesRead) { 36 | var start = reader.Position; 37 | 38 | var name = reader.ReadNullTerminatedString(255); 39 | if (name == "") { 40 | channel = null; 41 | bytesRead = reader.Position - start; 42 | return false; 43 | } 44 | 45 | channel = new Channel( 46 | name, 47 | (PixelType)reader.ReadInt32(), 48 | reader.ReadByte() != 0, 49 | reader.ReadByte(), reader.ReadByte(), reader.ReadByte(), 50 | reader.ReadInt32(), reader.ReadInt32()); 51 | 52 | bytesRead = reader.Position - start; 53 | return true; 54 | } 55 | 56 | public IEnumerator GetEnumerator() { 57 | return Channels.GetEnumerator(); 58 | } 59 | 60 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 61 | return GetEnumerator(); 62 | } 63 | 64 | public Channel this[int index] { get { return Channels[index]; } set { Channels[index] = value; } } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SharpEXR/Compression/RLE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.Compression { 8 | public static class RLE { 9 | public static int Uncompress(IEXRReader reader, int count, byte[] uncompressed) { 10 | var end = reader.Position + count; 11 | var maxLen = uncompressed.Length; 12 | var offset = 0; 13 | 14 | while (reader.Position < end) { 15 | int runcount = (sbyte)reader.ReadByte(); 16 | if (runcount < 0) { 17 | // raw 18 | runcount = -runcount; 19 | if (offset + runcount >= maxLen) { 20 | reader.Position -= 1; 21 | return 0; 22 | } 23 | 24 | reader.CopyBytes(uncompressed, offset, runcount); 25 | offset += runcount; 26 | } 27 | else { 28 | // run length 29 | if (offset + runcount + 1 >= maxLen) { 30 | reader.Position -= 1; 31 | return 0; 32 | } 33 | 34 | var value = reader.ReadByte(); 35 | MemSet(uncompressed, offset, value, runcount + 1); 36 | offset += runcount + 1; 37 | } 38 | } 39 | 40 | return offset; 41 | } 42 | 43 | public static void MemSet(byte[] array, int offset, byte value, int count) { 44 | #if DOTNET 45 | if (array == null) { 46 | throw new ArgumentNullException("array"); 47 | } 48 | 49 | int block = 32; 50 | int index = offset; 51 | int end = index + Math.Min(block, count); 52 | 53 | //Fill the initial array 54 | while (index < end) { 55 | array[index++] = value; 56 | } 57 | 58 | end = offset + count; 59 | while (index < end) { 60 | Buffer.BlockCopy(array, 0, array, index, Math.Min(block, end - index)); 61 | index += block; 62 | block *= 2; 63 | } 64 | #else 65 | var end = offset + count; 66 | for (int i = offset; i < end; i++) { 67 | array[i] = value; 68 | } 69 | #endif 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /EXRViewer/CommandLineArguments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace EXRViewer { 9 | public static class CommandLineArguments { 10 | /// 11 | /// Parses command line arguments. 12 | /// 13 | /// String containing the arguments. Uses Environment.CommandLine if null. Does not allow escaped double quotes. 14 | /// List of arguments. 15 | public static List Parse(string arguments = null) { 16 | return Parse(false, arguments); 17 | } 18 | 19 | /// 20 | /// Parses command line arguments. 21 | /// 22 | /// If true a sequence of \" between double quotes will be a single quote. 23 | /// String containing the arguments. Uses Environment.CommandLine if null. 24 | /// List of arguments. 25 | public static List Parse(bool allowEscapedDoubleQuotes, string arguments = null) { 26 | Regex regex; 27 | bool removeFirst = false; 28 | 29 | if (arguments == null) { 30 | arguments = Environment.CommandLine; 31 | removeFirst = true; 32 | } 33 | List args = new List(); 34 | 35 | regex = new Regex( 36 | allowEscapedDoubleQuotes ? "\"((\\\\\"|[^\"])*)\"+|[^\\s]+" : "\"([^\"]*)\"+|[^\\s]+", 37 | RegexOptions.None 38 | ); 39 | 40 | foreach (Match match in regex.Matches(arguments)) { 41 | if (match.Success) { 42 | string s = match.Value.Trim(); 43 | if (match.Groups[1].Success) { 44 | if (allowEscapedDoubleQuotes) { 45 | s = match.Groups[1].Value.Replace("\\\"", "\""); 46 | } 47 | else { 48 | s = match.Groups[1].Value; 49 | } 50 | } 51 | else { 52 | s = match.Groups[0].Value.Trim(); 53 | } 54 | args.Add(s); 55 | } 56 | } 57 | 58 | if (removeFirst) { 59 | args.RemoveAt(0); 60 | } 61 | return args; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SharpEXR/EXRVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR { 8 | [Flags] 9 | public enum EXRVersionFlags { 10 | IsSinglePartTiled = 0x200, 11 | LongNames = 0x400, 12 | NonImageParts = 0x800, 13 | MultiPart = 0x1000 14 | } 15 | 16 | public struct EXRVersion { 17 | public readonly EXRVersionFlags Value; 18 | 19 | public EXRVersion(int version, bool multiPart, bool longNames, bool nonImageParts, bool isSingleTiled = false) { 20 | Value = (EXRVersionFlags)(version & 0xFF); 21 | 22 | if (version == 1) { 23 | if (multiPart || nonImageParts) { 24 | throw new EXRFormatException("Invalid or corrupt EXR version: Version 1 EXR files cannot be multi part or have non image parts."); 25 | } 26 | if (isSingleTiled) { 27 | Value |= EXRVersionFlags.IsSinglePartTiled; 28 | } 29 | if (longNames) { 30 | Value |= EXRVersionFlags.LongNames; 31 | } 32 | } 33 | else { 34 | if (isSingleTiled) { 35 | Value |= EXRVersionFlags.IsSinglePartTiled; 36 | } 37 | if (longNames) { 38 | Value |= EXRVersionFlags.LongNames; 39 | } 40 | if (nonImageParts) { 41 | Value |= EXRVersionFlags.NonImageParts; 42 | } 43 | if (multiPart) { 44 | Value |= EXRVersionFlags.MultiPart; 45 | } 46 | } 47 | 48 | Verify(); 49 | } 50 | 51 | public EXRVersion(int value) { 52 | Value = (EXRVersionFlags)value; 53 | Verify(); 54 | } 55 | 56 | private void Verify() { 57 | if (IsSinglePartTiled) { 58 | // below bits must be 0 59 | if (IsMultiPart || HasNonImageParts) { 60 | throw new EXRFormatException("Invalid or corrupt EXR version: Version's single part bit was set, but multi part and/or non image data bits were also set."); 61 | } 62 | } 63 | } 64 | 65 | public int Version { 66 | get { 67 | return (int)Value & 0xFF; 68 | } 69 | } 70 | 71 | public bool IsSinglePartTiled { 72 | get { 73 | return Value.HasFlag(EXRVersionFlags.IsSinglePartTiled); 74 | } 75 | } 76 | 77 | public bool HasLongNames { 78 | get { 79 | return Value.HasFlag(EXRVersionFlags.LongNames); 80 | } 81 | } 82 | 83 | public bool HasNonImageParts { 84 | get { 85 | return Value.HasFlag(EXRVersionFlags.NonImageParts); 86 | } 87 | } 88 | 89 | public bool IsMultiPart { 90 | get { 91 | return Value.HasFlag(EXRVersionFlags.MultiPart); 92 | } 93 | } 94 | 95 | public int MaxNameLength { 96 | get { 97 | return HasLongNames ? 255 : 31; 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /EXRViewer/EXRViewer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {26AB652C-EE76-4248-98BD-D125E258A544} 8 | Exe 9 | Properties 10 | EXRViewer 11 | EXRViewer 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | PreserveNewest 54 | 55 | 56 | PreserveNewest 57 | 58 | 59 | PreserveNewest 60 | 61 | 62 | 63 | 64 | {effacb6c-98d5-4b92-839b-30e9eb51531d} 65 | SharpEXR 66 | 67 | 68 | 69 | 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /SharpEXR/EXRReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SharpEXR { 9 | public interface IEXRReader : IDisposable { 10 | byte ReadByte(); 11 | int ReadInt32(); 12 | uint ReadUInt32(); 13 | Half ReadHalf(); 14 | float ReadSingle(); 15 | double ReadDouble(); 16 | string ReadNullTerminatedString(int maxLength); 17 | string ReadString(int length); 18 | string ReadString(); 19 | byte[] ReadBytes(int count); 20 | void CopyBytes(byte[] dest, int offset, int count); 21 | int Position { get; set; } 22 | } 23 | 24 | public class EXRReader : IDisposable, IEXRReader { 25 | private BinaryReader reader; 26 | 27 | public EXRReader(Stream stream, bool leaveOpen = false) 28 | : this(new BinaryReader(stream, Encoding.ASCII, leaveOpen)) { 29 | } 30 | 31 | public EXRReader(BinaryReader reader) { 32 | this.reader = reader; 33 | } 34 | 35 | public byte ReadByte() { 36 | return reader.ReadByte(); 37 | } 38 | 39 | public int ReadInt32() { 40 | return reader.ReadInt32(); 41 | } 42 | 43 | public uint ReadUInt32() { 44 | return reader.ReadUInt32(); 45 | } 46 | 47 | public Half ReadHalf() { 48 | return Half.ToHalf(reader.ReadUInt16()); 49 | } 50 | 51 | public float ReadSingle() { 52 | return reader.ReadSingle(); 53 | } 54 | 55 | public double ReadDouble() { 56 | return reader.ReadDouble(); 57 | } 58 | 59 | public string ReadNullTerminatedString(int maxLength) { 60 | var start = reader.BaseStream.Position; 61 | StringBuilder str = new StringBuilder(); 62 | byte b; 63 | while ((b = reader.ReadByte()) != 0) { 64 | if (reader.BaseStream.Position - start > maxLength) { 65 | throw new EXRFormatException("Null terminated string exceeded maximum length of " + maxLength + " bytes."); 66 | } 67 | str.Append((char)b); 68 | } 69 | return str.ToString(); 70 | } 71 | 72 | public string ReadString() { 73 | var len = ReadInt32(); 74 | return ReadString(len); 75 | } 76 | 77 | public string ReadString(int length) { 78 | StringBuilder str = new StringBuilder(); 79 | for (int i = 0; i < length; i++) { 80 | str.Append((char)reader.ReadByte()); 81 | } 82 | return str.ToString(); 83 | } 84 | 85 | public byte[] ReadBytes(int count) { 86 | return reader.ReadBytes(count); 87 | } 88 | 89 | public void CopyBytes(byte[] dest, int offset, int count) { 90 | int bytesRead = reader.BaseStream.Read(dest, offset, count); 91 | if (bytesRead != count) { 92 | throw new Exception("Less bytes read than expected"); 93 | } 94 | } 95 | 96 | #region IDisposable 97 | private bool disposed = false; 98 | 99 | public void Dispose() { 100 | Dispose(true); 101 | GC.SuppressFinalize(this); 102 | } 103 | 104 | protected virtual void Dispose(bool disposing) { 105 | if (disposed) 106 | return; 107 | 108 | if (disposing) { 109 | // Free any other managed objects here. 110 | try { 111 | reader.Dispose(); 112 | } 113 | catch { } 114 | } 115 | 116 | // Free any unmanaged objects here. 117 | disposed = true; 118 | } 119 | 120 | #if DOTNET 121 | ~EXRReader() { 122 | Dispose(false); 123 | } 124 | #endif 125 | #endregion 126 | 127 | public int Position { 128 | get { 129 | return (int)reader.BaseStream.Position; 130 | } 131 | set { 132 | reader.BaseStream.Seek(value, System.IO.SeekOrigin.Begin); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /SharpEXR/EXRHeader.cs: -------------------------------------------------------------------------------- 1 | using SharpEXR.AttributeTypes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SharpEXR { 9 | public class EXRHeader { 10 | public static readonly Chromaticities DefaultChromaticities = new Chromaticities( 11 | 0.6400f, 0.3300f, 12 | 0.3000f, 0.6000f, 13 | 0.1500f, 0.0600f, 14 | 0.3127f, 0.3290f 15 | ); 16 | 17 | public Dictionary Attributes { get; protected set; } 18 | 19 | public EXRHeader() { 20 | Attributes = new Dictionary(); 21 | } 22 | 23 | public void Read(EXRFile file, IEXRReader reader) { 24 | EXRAttribute attribute; 25 | while (EXRAttribute.Read(file, reader, out attribute)) { 26 | Attributes[attribute.Name] = attribute; 27 | } 28 | } 29 | 30 | public bool TryGetAttribute(string name, out T result) { 31 | EXRAttribute attr; 32 | if (!Attributes.TryGetValue(name, out attr)) { 33 | result = default(T); 34 | return false; 35 | } 36 | 37 | if (attr.Value == null) { 38 | result = default(T); // assign default value, which will be null for non-value types 39 | // return true if not a value type 40 | return !typeof(T).IsClass && !typeof(T).IsInterface && !typeof(T).IsArray; 41 | } 42 | else { 43 | if (typeof(T).IsAssignableFrom(attr.Value.GetType())) { 44 | result = (T)attr.Value; 45 | return true; 46 | } 47 | else { 48 | result = default(T); 49 | return false; 50 | } 51 | } 52 | } 53 | 54 | public bool IsEmpty { get { return Attributes.Count == 0; } } 55 | 56 | public int ChunkCount { 57 | get { 58 | int chunkCount; 59 | if (!TryGetAttribute("chunkCount", out chunkCount)) { 60 | throw new EXRFormatException("Invalid or corrupt EXR header: Missing chunkCount attribute."); 61 | } 62 | return chunkCount; 63 | } 64 | } 65 | 66 | public Box2I DataWindow { 67 | get { 68 | Box2I dataWindow; 69 | if (!TryGetAttribute("dataWindow", out dataWindow)) { 70 | throw new EXRFormatException("Invalid or corrupt EXR header: Missing dataWindow attribute."); 71 | } 72 | return dataWindow; 73 | } 74 | } 75 | 76 | public EXRCompression Compression { 77 | get { 78 | EXRCompression compression; 79 | if (!TryGetAttribute("compression", out compression)) { 80 | throw new EXRFormatException("Invalid or corrupt EXR header: Missing compression attribute."); 81 | } 82 | return compression; 83 | } 84 | } 85 | 86 | public PartType Type { 87 | get { 88 | PartType type; 89 | if (!TryGetAttribute("type", out type)) { 90 | throw new EXRFormatException("Invalid or corrupt EXR header: Missing type attribute."); 91 | } 92 | return type; 93 | } 94 | } 95 | 96 | public ChannelList Channels { 97 | get { 98 | ChannelList channels; 99 | if (!TryGetAttribute("channels", out channels)) { 100 | throw new EXRFormatException("Invalid or corrupt EXR header: Missing channels attribute."); 101 | } 102 | return channels; 103 | } 104 | } 105 | 106 | // TODO: Should we search like this? Spec is unclear 107 | public Chromaticities Chromaticities { 108 | get { 109 | foreach (var attr in Attributes.Values) { 110 | if (attr.Type == "chromaticities" && attr.Value is Chromaticities) { 111 | return (Chromaticities)attr.Value; 112 | } 113 | } 114 | return DefaultChromaticities; 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /SharpEXR/SharpEXR.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EFFACB6C-98D5-4B92-839B-30E9EB51531D} 8 | Library 9 | Properties 10 | SharpEXR 11 | SharpEXR 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | TRACE;DEBUG;DOTNET;PARALLEL 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE;DOTNET;PARALLEL 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 100 | -------------------------------------------------------------------------------- /SharpEXR/ColorSpace/Gamma.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpEXR.ColorSpace { 8 | public static class Gamma { 9 | /// 10 | /// Convert a gamma corrected color channel to a linear color channel. 11 | /// 12 | public static float Expand(float nonlinear) { 13 | return (float)Math.Pow(nonlinear, 2.2); 14 | } 15 | 16 | /// 17 | /// Convert a linear color channel to a gamma corrected color channel. 18 | /// 19 | public static float Compress(float linear) { 20 | return (float)Math.Pow(linear, 1.0 / 2.2); 21 | } 22 | 23 | /// 24 | /// Convert a gamma corrected color to a linear color. 25 | /// 26 | public static void Expand(ref tVec3 pColor) { 27 | pColor.X = Expand(pColor.X); 28 | pColor.Y = Expand(pColor.Y); 29 | pColor.Z = Expand(pColor.Z); 30 | } 31 | 32 | /// 33 | /// Convert a linear color to a gamma corrected color. 34 | /// 35 | public static void Compress(ref tVec3 pColor) { 36 | pColor.X = Compress(pColor.X); 37 | pColor.Y = Compress(pColor.Y); 38 | pColor.Z = Compress(pColor.Z); 39 | } 40 | 41 | /// 42 | /// Convert a gamma corrected color to a linear color. 43 | /// 44 | public static void Expand(ref float r, ref float g, ref float b) { 45 | r = Expand(r); 46 | g = Expand(g); 47 | b = Expand(b); 48 | } 49 | 50 | /// 51 | /// Convert a linear color to a gamma corrected color. 52 | /// 53 | public static void Compress(ref float r, ref float g, ref float b) { 54 | r = Compress(r); 55 | g = Compress(g); 56 | b = Compress(b); 57 | } 58 | 59 | /// 60 | /// Convert a gamma corrected color to a linear color. 61 | /// 62 | public static tVec3 Expand(float r, float g, float b) { 63 | var vec = new tVec3(r, g, b); 64 | Expand(ref vec); 65 | return vec; 66 | } 67 | 68 | /// 69 | /// Convert a linear color to a gamma corrected color. 70 | /// 71 | public static tVec3 Compress(float r, float g, float b) { 72 | var vec = new tVec3(r, g, b); 73 | Compress(ref vec); 74 | return vec; 75 | } 76 | 77 | /// 78 | /// Convert an sRGB color channel to a linear sRGB color channel. 79 | /// 80 | public static float Expand_sRGB(float nonlinear) { 81 | return (nonlinear <= 0.04045f) 82 | ? (nonlinear / 12.92f) 83 | : ((float)Math.Pow((nonlinear + 0.055f) / 1.055f, 2.4f)); 84 | } 85 | 86 | /// 87 | /// Convert a linear sRGB color channel to a sRGB color channel. 88 | /// 89 | public static float Compress_sRGB(float linear) { 90 | return (linear <= 0.0031308f) 91 | ? (12.92f * linear) 92 | : (1.055f * (float)Math.Pow(linear, 1.0f / 2.4f) - 0.055f); 93 | } 94 | 95 | /// 96 | /// Convert an sRGB color to a linear sRGB color. 97 | /// 98 | public static void Expand_sRGB(ref tVec3 pColor) { 99 | pColor.X = Expand_sRGB(pColor.X); 100 | pColor.Y = Expand_sRGB(pColor.Y); 101 | pColor.Z = Expand_sRGB(pColor.Z); 102 | } 103 | 104 | /// 105 | /// Convert a linear sRGB color to an sRGB color. 106 | /// 107 | public static void Compress_sRGB(ref tVec3 pColor) { 108 | pColor.X = Compress_sRGB(pColor.X); 109 | pColor.Y = Compress_sRGB(pColor.Y); 110 | pColor.Z = Compress_sRGB(pColor.Z); 111 | } 112 | 113 | /// 114 | /// Convert an sRGB color to a linear sRGB color. 115 | /// 116 | public static void Expand_sRGB(ref float r, ref float g, ref float b) { 117 | r = Expand_sRGB(r); 118 | g = Expand_sRGB(g); 119 | b = Expand_sRGB(b); 120 | } 121 | 122 | /// 123 | /// Convert a linear sRGB color to an sRGB color. 124 | /// 125 | public static void Compress_sRGB(ref float r, ref float g, ref float b) { 126 | r = Compress_sRGB(r); 127 | g = Compress_sRGB(g); 128 | b = Compress_sRGB(b); 129 | } 130 | 131 | /// 132 | /// Convert an sRGB color to a linear sRGB color. 133 | /// 134 | public static tVec3 Expand_sRGB(float r, float g, float b) { 135 | var vec = new tVec3(r, g, b); 136 | Expand_sRGB(ref vec); 137 | return vec; 138 | } 139 | 140 | /// 141 | /// Convert a linear sRGB color to an sRGB color. 142 | /// 143 | public static tVec3 Compress_sRGB(float r, float g, float b) { 144 | var vec = new tVec3(r, g, b); 145 | Compress_sRGB(ref vec); 146 | return vec; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /SharpEXR/EXRFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SharpEXR { 9 | public class EXRFile { 10 | public EXRVersion Version { get; protected set; } 11 | public List Headers { get; protected set; } 12 | public List OffsetTables { get; protected set; } 13 | public List Parts { get; protected set; } 14 | 15 | public EXRFile() { 16 | } 17 | 18 | public void Read(IEXRReader reader) { 19 | // first four bytes of an OpenEXR file are always 0x76, 0x2f, 0x31 and 0x01 or 20000630 20 | var magicNumber = reader.ReadInt32(); 21 | if (magicNumber != 20000630) { 22 | throw new EXRFormatException("Invalid or corrupt EXR layout: First four bytes were not 20000630."); 23 | } 24 | 25 | var versionValue = reader.ReadInt32(); 26 | Version = new EXRVersion(versionValue); 27 | 28 | Headers = new List(); 29 | if (Version.IsMultiPart) { 30 | while (true) { 31 | var header = new EXRHeader(); 32 | header.Read(this, reader); 33 | if (header.IsEmpty) { 34 | break; 35 | } 36 | Headers.Add(header); 37 | } 38 | throw new NotImplementedException("Multi part EXR files are not currently supported"); 39 | } 40 | else { 41 | if (Version.IsSinglePartTiled) { 42 | throw new NotImplementedException("Tiled EXR files are not currently supported"); 43 | } 44 | 45 | var header = new EXRHeader(); 46 | header.Read(this, reader); 47 | Headers.Add(header); 48 | } 49 | 50 | OffsetTables = new List(); 51 | foreach (var header in Headers) { 52 | int offsetTableSize; 53 | 54 | if (Version.IsMultiPart) { 55 | offsetTableSize = header.ChunkCount; 56 | } 57 | else if (Version.IsSinglePartTiled) { 58 | // TODO: Implement 59 | offsetTableSize = 0; 60 | } 61 | else { 62 | var compression = header.Compression; 63 | var dataWindow = header.DataWindow; 64 | var linesPerBlock = GetScanLinesPerBlock(compression); 65 | var blockCount = (int)Math.Ceiling(dataWindow.Height / (double)linesPerBlock); 66 | 67 | offsetTableSize = blockCount; 68 | } 69 | 70 | var table = new OffsetTable(offsetTableSize); 71 | table.Read(reader, offsetTableSize); 72 | OffsetTables.Add(table); 73 | } 74 | } 75 | 76 | public static int GetScanLinesPerBlock(EXRCompression compression) { 77 | switch (compression) { 78 | default: 79 | return 1; 80 | case EXRCompression.ZIP: 81 | case EXRCompression.PXR24: 82 | return 16; 83 | case EXRCompression.PIZ: 84 | case EXRCompression.B44: 85 | case EXRCompression.B44A: 86 | return 32; 87 | } 88 | } 89 | 90 | public static int GetBytesPerPixel(ImageDestFormat format) { 91 | switch (format) { 92 | case ImageDestFormat.RGB16: 93 | case ImageDestFormat.BGR16: 94 | return 6; 95 | case ImageDestFormat.RGB32: 96 | case ImageDestFormat.BGR32: 97 | return 12; 98 | case ImageDestFormat.RGB8: 99 | case ImageDestFormat.BGR8: 100 | return 3; 101 | case ImageDestFormat.PremultipliedRGBA16: 102 | case ImageDestFormat.PremultipliedBGRA16: 103 | case ImageDestFormat.RGBA16: 104 | case ImageDestFormat.BGRA16: 105 | return 8; 106 | case ImageDestFormat.PremultipliedRGBA32: 107 | case ImageDestFormat.PremultipliedBGRA32: 108 | case ImageDestFormat.RGBA32: 109 | case ImageDestFormat.BGRA32: 110 | return 16; 111 | case ImageDestFormat.PremultipliedRGBA8: 112 | case ImageDestFormat.PremultipliedBGRA8: 113 | case ImageDestFormat.RGBA8: 114 | case ImageDestFormat.BGRA8: 115 | return 4; 116 | } 117 | throw new ArgumentException("Unrecognized destination format", "format"); 118 | } 119 | 120 | public static int GetBitsPerPixel(ImageDestFormat format) { 121 | switch (format) { 122 | case ImageDestFormat.PremultipliedRGBA32: 123 | case ImageDestFormat.PremultipliedBGRA32: 124 | case ImageDestFormat.RGBA32: 125 | case ImageDestFormat.BGRA32: 126 | case ImageDestFormat.RGB32: 127 | case ImageDestFormat.BGR32: 128 | return 32; 129 | case ImageDestFormat.PremultipliedRGBA8: 130 | case ImageDestFormat.PremultipliedBGRA8: 131 | case ImageDestFormat.RGBA8: 132 | case ImageDestFormat.BGRA8: 133 | case ImageDestFormat.RGB8: 134 | case ImageDestFormat.BGR8: 135 | return 8; 136 | case ImageDestFormat.RGB16: 137 | case ImageDestFormat.BGR16: 138 | case ImageDestFormat.PremultipliedRGBA16: 139 | case ImageDestFormat.PremultipliedBGRA16: 140 | case ImageDestFormat.RGBA16: 141 | case ImageDestFormat.BGRA16: 142 | return 16; 143 | } 144 | throw new ArgumentException("Unrecognized destination format", "format"); 145 | } 146 | 147 | #if DOTNET 148 | public static EXRFile FromFile(string file) { 149 | var reader = new EXRReader(new FileStream(file, FileMode.Open, FileAccess.Read)); 150 | var result = FromReader(reader); 151 | reader.Dispose(); 152 | return result; 153 | } 154 | #endif 155 | 156 | public static EXRFile FromStream(Stream stream) { 157 | var reader = new EXRReader(new BinaryReader(stream)); 158 | var result = FromReader(reader); 159 | reader.Dispose(); 160 | return result; 161 | } 162 | 163 | public static EXRFile FromReader(IEXRReader reader) { 164 | var img = new EXRFile(); 165 | img.Read(reader); 166 | 167 | img.Parts = new List(); 168 | for (int i = 0; i < img.Headers.Count; i++) { 169 | var part = new EXRPart(img.Version, img.Headers[i], img.OffsetTables[i]); 170 | img.Parts.Add(part); 171 | //part.ReadPixelData(reader); 172 | } 173 | 174 | return img; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /SharpEXR/HalfHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SharpEXR { 5 | /// 6 | /// Helper class for Half conversions and some low level operations. 7 | /// This class is internally used in the Half class. 8 | /// 9 | /// 10 | /// References: 11 | /// - Fast Half Float Conversions, Jeroen van der Zijp, link: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf 12 | /// 13 | [ComVisible(false)] 14 | internal static class HalfHelper { 15 | private static uint[] mantissaTable = GenerateMantissaTable(); 16 | private static uint[] exponentTable = GenerateExponentTable(); 17 | private static ushort[] offsetTable = GenerateOffsetTable(); 18 | private static ushort[] baseTable = GenerateBaseTable(); 19 | private static sbyte[] shiftTable = GenerateShiftTable(); 20 | 21 | // Transforms the subnormal representation to a normalized one. 22 | private static uint ConvertMantissa(int i) { 23 | uint m = (uint)(i << 13); // Zero pad mantissa bits 24 | uint e = 0; // Zero exponent 25 | 26 | // While not normalized 27 | while ((m & 0x00800000) == 0) { 28 | e -= 0x00800000; // Decrement exponent (1<<23) 29 | m <<= 1; // Shift mantissa 30 | } 31 | m &= unchecked((uint)~0x00800000); // Clear leading 1 bit 32 | e += 0x38800000; // Adjust bias ((127-14)<<23) 33 | return m | e; // Return combined number 34 | } 35 | 36 | private static uint[] GenerateMantissaTable() { 37 | uint[] mantissaTable = new uint[2048]; 38 | mantissaTable[0] = 0; 39 | for (int i = 1; i < 1024; i++) { 40 | mantissaTable[i] = ConvertMantissa(i); 41 | } 42 | for (int i = 1024; i < 2048; i++) { 43 | mantissaTable[i] = (uint)(0x38000000 + ((i - 1024) << 13)); 44 | } 45 | 46 | return mantissaTable; 47 | } 48 | private static uint[] GenerateExponentTable() { 49 | uint[] exponentTable = new uint[64]; 50 | exponentTable[0] = 0; 51 | for (int i = 1; i < 31; i++) { 52 | exponentTable[i] = (uint)(i << 23); 53 | } 54 | exponentTable[31] = 0x47800000; 55 | exponentTable[32] = 0x80000000; 56 | for (int i = 33; i < 63; i++) { 57 | exponentTable[i] = (uint)(0x80000000 + ((i - 32) << 23)); 58 | } 59 | exponentTable[63] = 0xc7800000; 60 | 61 | return exponentTable; 62 | } 63 | private static ushort[] GenerateOffsetTable() { 64 | ushort[] offsetTable = new ushort[64]; 65 | offsetTable[0] = 0; 66 | for (int i = 1; i < 32; i++) { 67 | offsetTable[i] = 1024; 68 | } 69 | offsetTable[32] = 0; 70 | for (int i = 33; i < 64; i++) { 71 | offsetTable[i] = 1024; 72 | } 73 | 74 | return offsetTable; 75 | } 76 | private static ushort[] GenerateBaseTable() { 77 | ushort[] baseTable = new ushort[512]; 78 | for (int i = 0; i < 256; ++i) { 79 | sbyte e = (sbyte)(127 - i); 80 | if (e > 24) { // Very small numbers map to zero 81 | baseTable[i | 0x000] = 0x0000; 82 | baseTable[i | 0x100] = 0x8000; 83 | } 84 | else if (e > 14) { // Small numbers map to denorms 85 | baseTable[i | 0x000] = (ushort)(0x0400 >> (18 + e)); 86 | baseTable[i | 0x100] = (ushort)((0x0400 >> (18 + e)) | 0x8000); 87 | } 88 | else if (e >= -15) { // Normal numbers just lose precision 89 | baseTable[i | 0x000] = (ushort)((15 - e) << 10); 90 | baseTable[i | 0x100] = (ushort)(((15 - e) << 10) | 0x8000); 91 | } 92 | else if (e > -128) { // Large numbers map to Infinity 93 | baseTable[i | 0x000] = 0x7c00; 94 | baseTable[i | 0x100] = 0xfc00; 95 | } 96 | else { // Infinity and NaN's stay Infinity and NaN's 97 | baseTable[i | 0x000] = 0x7c00; 98 | baseTable[i | 0x100] = 0xfc00; 99 | } 100 | } 101 | 102 | return baseTable; 103 | } 104 | private static sbyte[] GenerateShiftTable() { 105 | sbyte[] shiftTable = new sbyte[512]; 106 | for (int i = 0; i < 256; ++i) { 107 | sbyte e = (sbyte)(127 - i); 108 | if (e > 24) { // Very small numbers map to zero 109 | shiftTable[i | 0x000] = 24; 110 | shiftTable[i | 0x100] = 24; 111 | } 112 | else if (e > 14) { // Small numbers map to denorms 113 | shiftTable[i | 0x000] = (sbyte)(e - 1); 114 | shiftTable[i | 0x100] = (sbyte)(e - 1); 115 | } 116 | else if (e >= -15) { // Normal numbers just lose precision 117 | shiftTable[i | 0x000] = 13; 118 | shiftTable[i | 0x100] = 13; 119 | } 120 | else if (e > -128) { // Large numbers map to Infinity 121 | shiftTable[i | 0x000] = 24; 122 | shiftTable[i | 0x100] = 24; 123 | } 124 | else { // Infinity and NaN's stay Infinity and NaN's 125 | shiftTable[i | 0x000] = 13; 126 | shiftTable[i | 0x100] = 13; 127 | } 128 | } 129 | 130 | return shiftTable; 131 | } 132 | 133 | #if UNSAFE 134 | public static unsafe float HalfToSingle(Half half) { 135 | #else 136 | public static float HalfToSingle(Half half) { 137 | #endif 138 | uint result = mantissaTable[offsetTable[half.value >> 10] + (half.value & 0x3ff)] + exponentTable[half.value >> 10]; 139 | 140 | #if !UNSAFE 141 | #if DOTNET 142 | var bytes = BitConverter.GetBytes(result); 143 | return BitConverter.ToSingle(bytes, 0); 144 | #else 145 | buffer.WriteUInt32(result, 0); 146 | return buffer.ReadSingle(0); 147 | #endif 148 | #else 149 | return *((float*)&result); 150 | #endif 151 | } 152 | 153 | #if UNSAFE 154 | public static unsafe Half SingleToHalf(float single) { 155 | uint value = *((uint*)&single); 156 | #else 157 | #if DOTNET 158 | public static Half SingleToHalf(float single) { 159 | var bytes = BitConverter.GetBytes(single); 160 | var value = BitConverter.ToUInt32(bytes, 0); 161 | #else 162 | static Franca.BinaryData buffer = new Franca.BinaryData(); 163 | public static Half SingleToHalf(float single) { 164 | buffer.WriteSingle(single, 0); 165 | var value = buffer.ReadUInt32(0); 166 | #endif 167 | #endif 168 | 169 | ushort result = (ushort)(baseTable[(value >> 23) & 0x1ff] + ((value & 0x007fffff) >> shiftTable[value >> 23])); 170 | return Half.ToHalf(result); 171 | } 172 | 173 | public static Half Negate(Half half) { 174 | return Half.ToHalf((ushort)(half.value ^ 0x8000)); 175 | } 176 | public static Half Abs(Half half) { 177 | return Half.ToHalf((ushort)(half.value & 0x7fff)); 178 | } 179 | 180 | public static bool IsNaN(Half half) { 181 | return ((half.value & 0x7fff) > 0x7c00); 182 | } 183 | public static bool IsInfinity(Half half) { 184 | return ((half.value & 0x7fff) == 0x7c00); 185 | } 186 | public static bool IsPositiveInfinity(Half half) { 187 | return (half.value == 0x7c00); 188 | } 189 | public static bool IsNegativeInfinity(Half half) { 190 | return (half.value == 0xfc00); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /SharpEXR/ColorSpace/XYZ.cs: -------------------------------------------------------------------------------- 1 | using SharpEXR.AttributeTypes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | // source http://www.ryanjuckett.com/programming/rgb-color-space-conversion/ 9 | namespace SharpEXR.ColorSpace { 10 | public struct tVec2 { 11 | public float X; 12 | public float Y; 13 | 14 | public tVec2(float x, float y) { 15 | X = x; 16 | Y = y; 17 | } 18 | } 19 | public struct tVec3 { 20 | public float X; 21 | public float Y; 22 | public float Z; 23 | 24 | public tVec3(float x, float y, float z) { 25 | X = x; 26 | Y = y; 27 | Z = z; 28 | } 29 | } 30 | public struct tMat3x3 { 31 | public float 32 | M00, M01, M02, 33 | M10, M11, M12, 34 | M20, M21, M22; 35 | 36 | public void SetCol(int colIdx, tVec3 vec) { 37 | this[0, colIdx] = vec.X; 38 | this[1, colIdx] = vec.Y; 39 | this[2, colIdx] = vec.Z; 40 | } 41 | 42 | public bool Invert(out tMat3x3 result) { 43 | result = default(tMat3x3); 44 | 45 | // calculate the minors for the first row 46 | var minor00 = this[1, 1] * this[2, 2] - this[1, 2] * this[2, 1]; 47 | var minor01 = this[1, 2] * this[2, 0] - this[1, 0] * this[2, 2]; 48 | var minor02 = this[1, 0] * this[2, 1] - this[1, 1] * this[2, 0]; 49 | 50 | // calculate the determinant 51 | var determinant = this[0, 0] * minor00 + 52 | this[0, 1] * minor01 + 53 | this[0, 2] * minor02; 54 | 55 | // check if the input is a singular matrix (non-invertable) 56 | // (note that the epsilon here was arbitrarily chosen) 57 | if (determinant > -0.000001f && determinant < 0.000001f) { 58 | return false; 59 | } 60 | 61 | // the inverse of inMat is (1 / determinant) * adjoint(inMat) 62 | var invDet = 1.0f / determinant; 63 | result[0, 0] = invDet * minor00; 64 | result[0, 1] = invDet * (this[2, 1] * this[0, 2] - this[2, 2] * this[0, 1]); 65 | result[0, 2] = invDet * (this[0, 1] * this[1, 2] - this[0, 2] * this[1, 1]); 66 | 67 | result[1, 0] = invDet * minor01; 68 | result[1, 1] = invDet * (this[2, 2] * this[0, 0] - this[2, 0] * this[0, 2]); 69 | result[1, 2] = invDet * (this[0, 2] * this[1, 0] - this[0, 0] * this[1, 2]); 70 | 71 | result[2, 0] = invDet * minor02; 72 | result[2, 1] = invDet * (this[2, 0] * this[0, 1] - this[2, 1] * this[0, 0]); 73 | result[2, 2] = invDet * (this[0, 0] * this[1, 1] - this[0, 1] * this[1, 0]); 74 | 75 | return true; 76 | } 77 | 78 | public static tVec3 operator *(tMat3x3 mat, tVec3 vec) { 79 | return new tVec3( 80 | mat[0, 0] * vec.X + mat[0, 1] * vec.Y + mat[0, 2] * vec.Z, 81 | mat[1, 0] * vec.X + mat[1, 1] * vec.Y + mat[1, 2] * vec.Z, 82 | mat[2, 0] * vec.X + mat[2, 1] * vec.Y + mat[2, 2] * vec.Z 83 | ); 84 | } 85 | 86 | public float this[int row, int col] { 87 | get { 88 | switch (row) { 89 | case 0: 90 | switch (col) { 91 | default: 92 | return M00; 93 | case 1: 94 | return M01; 95 | case 2: 96 | return M02; 97 | } 98 | case 1: 99 | switch (col) { 100 | default: 101 | return M10; 102 | case 1: 103 | return M11; 104 | case 2: 105 | return M12; 106 | } 107 | default: 108 | switch (col) { 109 | default: 110 | return M20; 111 | case 1: 112 | return M21; 113 | case 2: 114 | return M22; 115 | } 116 | } 117 | } 118 | set { 119 | switch (row) { 120 | case 0: 121 | switch (col) { 122 | default: 123 | M00 = value; 124 | break; 125 | case 1: 126 | M01 = value; 127 | break; 128 | case 2: 129 | M02 = value; 130 | break; 131 | } 132 | break; 133 | case 1: 134 | switch (col) { 135 | default: 136 | M10 = value; 137 | break; 138 | case 1: 139 | M11 = value; 140 | break; 141 | case 2: 142 | M12 = value; 143 | break; 144 | } 145 | break; 146 | default: 147 | switch (col) { 148 | default: 149 | M20 = value; 150 | break; 151 | case 1: 152 | M21 = value; 153 | break; 154 | case 2: 155 | M22 = value; 156 | break; 157 | } 158 | break; 159 | } 160 | } 161 | } 162 | } 163 | 164 | public static class XYZ { 165 | /// 166 | /// Convert a linear sRGB color to an sRGB color 167 | /// 168 | public static tMat3x3 CalcColorSpaceConversion_RGB_to_XYZ(Chromaticities chromaticities) { 169 | return CalcColorSpaceConversion_RGB_to_XYZ( 170 | new tVec2(chromaticities.RedX, chromaticities.RedY), 171 | new tVec2(chromaticities.GreenX, chromaticities.GreenY), 172 | new tVec2(chromaticities.BlueX, chromaticities.BlueY), 173 | new tVec2(chromaticities.WhiteX, chromaticities.WhiteY)); 174 | } 175 | 176 | /// 177 | /// Convert a linear sRGB color to an sRGB color 178 | /// 179 | public static tMat3x3 CalcColorSpaceConversion_RGB_to_XYZ 180 | ( 181 | tVec2 red_xy, // xy chromaticity coordinates of the red primary 182 | tVec2 green_xy, // xy chromaticity coordinates of the green primary 183 | tVec2 blue_xy, // xy chromaticity coordinates of the blue primary 184 | tVec2 white_xy // xy chromaticity coordinates of the white point 185 | ) 186 | { 187 | tMat3x3 pOutput = new tMat3x3(); 188 | 189 | // generate xyz chromaticity coordinates (x + y + z = 1) from xy coordinates 190 | tVec3 r = new tVec3(red_xy.X, red_xy.Y, 1.0f - (red_xy.X + red_xy.Y)); 191 | tVec3 g = new tVec3(green_xy.X, green_xy.Y, 1.0f - (green_xy.X + green_xy.Y)); 192 | tVec3 b = new tVec3(blue_xy.X, blue_xy.Y, 1.0f - (blue_xy.X + blue_xy.Y)); 193 | tVec3 w = new tVec3(white_xy.X, white_xy.Y, 1.0f - (white_xy.X + white_xy.Y)); 194 | 195 | // Convert white xyz coordinate to XYZ coordinate by letting that the white 196 | // point have and XYZ relative luminance of 1.0. Relative luminance is the Y 197 | // component of and XYZ color. 198 | // XYZ = xyz * (Y / y) 199 | w.X /= white_xy.Y; 200 | w.Y /= white_xy.Y; 201 | w.Z /= white_xy.Y; 202 | 203 | // Solve for the transformation matrix 'M' from RGB to XYZ 204 | // * We know that the columns of M are equal to the unknown XYZ values of r, g and b. 205 | // * We know that the XYZ values of r, g and b are each a scaled version of the known 206 | // corresponding xyz chromaticity values. 207 | // * We know the XYZ value of white based on its xyz value and the assigned relative 208 | // luminance of 1.0. 209 | // * We know the RGB value of white is (1,1,1). 210 | // 211 | // white_XYZ = M * white_RGB 212 | // 213 | // [r.x g.x b.x] 214 | // N = [r.y g.y b.y] 215 | // [r.z g.z b.z] 216 | // 217 | // [sR 0 0 ] 218 | // S = [0 sG 0 ] 219 | // [0 0 sB] 220 | // 221 | // M = N * S 222 | // white_XYZ = N * S * white_RGB 223 | // N^-1 * white_XYZ = S * white_RGB = (sR,sG,sB) 224 | // 225 | // We now have an equation for the components of the scale matrix 'S' and 226 | // can compute 'M' from 'N' and 'S' 227 | 228 | pOutput.SetCol(0, r); 229 | pOutput.SetCol(1, g); 230 | pOutput.SetCol(2, b); 231 | 232 | tMat3x3 invMat; 233 | pOutput.Invert(out invMat); 234 | 235 | tVec3 scale = invMat * w; 236 | 237 | pOutput[0, 0] *= scale.X; 238 | pOutput[1, 0] *= scale.X; 239 | pOutput[2, 0] *= scale.X; 240 | 241 | pOutput[0, 1] *= scale.Y; 242 | pOutput[1, 1] *= scale.Y; 243 | pOutput[2, 1] *= scale.Y; 244 | 245 | pOutput[0, 2] *= scale.Z; 246 | pOutput[1, 2] *= scale.Z; 247 | pOutput[2, 2] *= scale.Z; 248 | 249 | return pOutput; 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /SharpEXR/EXRAttribute.cs: -------------------------------------------------------------------------------- 1 | using SharpEXR.AttributeTypes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace SharpEXR { 9 | public class EXRAttribute { 10 | public string Name { get; protected set; } 11 | public string Type { get; protected set; } 12 | public int Size { get; protected set; } 13 | public object Value { get; protected set; } 14 | 15 | public EXRAttribute() { 16 | } 17 | 18 | public static bool Read(EXRFile file, IEXRReader reader, out EXRAttribute attribute) { 19 | attribute = new EXRAttribute(); 20 | return attribute.Read(file, reader); 21 | } 22 | 23 | public override string ToString() { 24 | return Value.ToString(); 25 | } 26 | 27 | /// 28 | /// Returns true unless this is the end of the header 29 | /// 30 | public bool Read(EXRFile file, IEXRReader reader) { 31 | var maxLen = file.Version.MaxNameLength; 32 | 33 | try { 34 | Name = reader.ReadNullTerminatedString(maxLen); 35 | } 36 | catch (Exception e) { 37 | throw new EXRFormatException("Invalid or corrupt EXR header attribute name: " + e.Message, e); 38 | } 39 | if (Name == "") { 40 | return false; 41 | } 42 | 43 | try { 44 | Type = reader.ReadNullTerminatedString(maxLen); 45 | } 46 | catch (Exception e) { 47 | throw new EXRFormatException("Invalid or corrupt EXR header attribute type for '" + Name + "': " + e.Message, e); 48 | } 49 | if (Type == "") { 50 | throw new EXRFormatException("Invalid or corrupt EXR header attribute type for '" + Name + "': Cannot be an empty string."); 51 | } 52 | 53 | Size = reader.ReadInt32(); 54 | 55 | switch (Type) { 56 | case "box2i": 57 | if (Size != 16) { 58 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type box2i: Size must be 16 bytes, was " + Size + "."); 59 | } 60 | Value = new Box2I(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); 61 | break; 62 | case "box2f": 63 | if (Size != 16) { 64 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type box2f: Size must be 16 bytes, was " + Size + "."); 65 | } 66 | Value = new Box2F(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 67 | break; 68 | case "chromaticities": 69 | if (Size != 32) { 70 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type chromaticities: Size must be 32 bytes, was " + Size + "."); 71 | } 72 | Value = new Chromaticities(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 73 | break; 74 | case "compression": 75 | if (Size != 1) { 76 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type compression: Size must be 1 byte, was " + Size + "."); 77 | } 78 | Value = (EXRCompression)reader.ReadByte(); 79 | break; 80 | case "double": 81 | if (Size != 8) { 82 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type double: Size must be 8 bytes, was " + Size + "."); 83 | } 84 | Value = reader.ReadDouble(); 85 | break; 86 | case "envmap": 87 | if (Size != 1) { 88 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type envmap: Size must be 1 byte, was " + Size + "."); 89 | } 90 | Value = (EnvMap)reader.ReadByte(); 91 | break; 92 | case "float": 93 | if (Size != 4) { 94 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type float: Size must be 4 bytes, was " + Size + "."); 95 | } 96 | Value = reader.ReadSingle(); 97 | break; 98 | case "int": 99 | if (Size != 4) { 100 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type int: Size must be 4 bytes, was " + Size + "."); 101 | } 102 | Value = reader.ReadInt32(); 103 | break; 104 | case "keycode": 105 | if (Size != 28) { 106 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type keycode: Size must be 28 bytes, was " + Size + "."); 107 | } 108 | Value = new KeyCode(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); 109 | break; 110 | case "lineOrder": 111 | if (Size != 1) { 112 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type lineOrder: Size must be 1 byte, was " + Size + "."); 113 | } 114 | Value = (LineOrder)reader.ReadByte(); 115 | break; 116 | case "m33f": 117 | if (Size != 36) { 118 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type m33f: Size must be 36 bytes, was " + Size + "."); 119 | } 120 | Value = new M33F( 121 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), 122 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), 123 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 124 | break; 125 | case "m44f": 126 | if (Size != 64) { 127 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type m44f: Size must be 64 bytes, was " + Size + "."); 128 | } 129 | Value = new M44F( 130 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), 131 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), 132 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), 133 | reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 134 | break; 135 | case "rational": 136 | if (Size != 8) { 137 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type rational: Size must be 8 bytes, was " + Size + "."); 138 | } 139 | Value = new Rational(reader.ReadInt32(), reader.ReadUInt32()); 140 | break; 141 | case "string": 142 | if (Size < 0) { 143 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type string: Invalid Size, was " + Size + "."); 144 | } 145 | Value = reader.ReadString(Size); 146 | break; 147 | case "stringvector": 148 | if (Size == 0) { 149 | Value = new List(); 150 | } 151 | else if (Size < 4) { 152 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type stringvector: Size must be at least 4 bytes or 0 bytes, was " + Size + "."); 153 | } 154 | else { 155 | var strings = new List(); 156 | Value = strings; 157 | var bytesRead = 0; 158 | 159 | while (bytesRead < Size) { 160 | var loc = reader.Position; 161 | var str = reader.ReadString(); 162 | strings.Add(str); 163 | bytesRead += reader.Position - loc; 164 | } 165 | 166 | if (bytesRead != Size) { 167 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type stringvector: Read " + bytesRead + " bytes but Size was " + Size + "."); 168 | } 169 | } 170 | break; 171 | case "tiledesc": 172 | if (Size != 9) { 173 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type tiledesc: Size must be 9 bytes, was " + Size + "."); 174 | } 175 | Value = new TileDesc(reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadByte()); 176 | break; 177 | case "timecode": 178 | if (Size != 8) { 179 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type timecode: Size must be 8 bytes, was " + Size + "."); 180 | } 181 | Value = new TimeCode(reader.ReadUInt32(), reader.ReadUInt32()); 182 | break; 183 | case "v2i": 184 | if (Size != 8) { 185 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type v2i: Size must be 8 bytes, was " + Size + "."); 186 | } 187 | Value = new V2I(reader.ReadInt32(), reader.ReadInt32()); 188 | break; 189 | case "v2f": 190 | if (Size != 8) { 191 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type v2f: Size must be 8 bytes, was " + Size + "."); 192 | } 193 | Value = new V2F(reader.ReadSingle(), reader.ReadSingle()); 194 | break; 195 | case "v3i": 196 | if (Size != 12) { 197 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type v3i: Size must be 12 bytes, was " + Size + "."); 198 | } 199 | Value = new V3I(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); 200 | break; 201 | case "v3f": 202 | if (Size != 12) { 203 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type v3f: Size must be 12 bytes, was " + Size + "."); 204 | } 205 | Value = new V3F(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 206 | break; 207 | case "chlist": 208 | var chlist = new ChannelList(); 209 | try { 210 | chlist.Read(file, reader, Size); 211 | } 212 | catch (Exception e) { 213 | throw new EXRFormatException("Invalid or corrupt EXR header attribute '" + Name + "' of type chlist: " + e.Message, e); 214 | } 215 | Value = chlist; 216 | break; 217 | case "preview": 218 | default: 219 | Value = reader.ReadBytes(Size); 220 | break; 221 | } 222 | 223 | return true; 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /SharpEXR/EXRPart.cs: -------------------------------------------------------------------------------- 1 | using SharpEXR.AttributeTypes; 2 | using SharpEXR.ColorSpace; 3 | using SharpEXR.Compression; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace SharpEXR { 12 | public delegate IEXRReader ParallelReaderCreationDelegate(); 13 | 14 | public class EXRPart { 15 | public readonly EXRVersion Version; 16 | public readonly EXRHeader Header; 17 | public readonly OffsetTable Offsets; 18 | public readonly PartType Type; 19 | 20 | public readonly Box2I DataWindow; 21 | private bool hasData; 22 | 23 | private Dictionary floatChannels; 24 | public Dictionary FloatChannels { 25 | get { 26 | return floatChannels; 27 | } 28 | protected set { 29 | floatChannels = value; 30 | } 31 | } 32 | private Dictionary halfChannels; 33 | public Dictionary HalfChannels { 34 | get { 35 | return halfChannels; 36 | } 37 | protected set { 38 | halfChannels = value; 39 | } 40 | } 41 | 42 | public EXRPart(EXRVersion version, EXRHeader header, OffsetTable offsets) { 43 | Version = version; 44 | Header = header; 45 | Offsets = offsets; 46 | 47 | if (Version.IsMultiPart) { 48 | Type = header.Type; 49 | } 50 | else { 51 | Type = version.IsSinglePartTiled ? PartType.Tiled : PartType.ScanLine; 52 | } 53 | 54 | DataWindow = Header.DataWindow; 55 | FloatChannels = new Dictionary(); 56 | HalfChannels = new Dictionary(); 57 | 58 | foreach (var channel in header.Channels) { 59 | if (channel.Type == PixelType.Float) { 60 | FloatChannels[channel.Name] = new float[DataWindow.Width * DataWindow.Height]; 61 | } 62 | else if (channel.Type == PixelType.Half) { 63 | HalfChannels[channel.Name] = new Half[DataWindow.Width * DataWindow.Height]; 64 | } 65 | else { 66 | throw new NotImplementedException("Only 16 and 32 bit floating point EXR images are supported."); 67 | } 68 | } 69 | } 70 | 71 | protected virtual void CheckHasData() { 72 | if (!hasData) { 73 | throw new InvalidOperationException("Call EXRPart.Open before performing image operations."); 74 | } 75 | } 76 | 77 | /// 78 | /// Gets RGB color channels as an interleaved array of Halfs. If this EXRPart's HasAlpha property is true, 79 | /// this will include the alpha channel in the array. 80 | /// 81 | public Half[] GetHalfs(ChannelConfiguration channels, bool premultiplied, GammaEncoding gamma) { 82 | return GetHalfs(channels, premultiplied, gamma, HasAlpha); 83 | } 84 | 85 | /// 86 | /// Gets RGB color channels as an interleaved array of Halfs. If includeAlpha is true, 87 | /// this will include the alpha channel in the array. 88 | /// 89 | public Half[] GetHalfs(ChannelConfiguration channels, bool premultiplied, GammaEncoding gamma, bool includeAlpha) { 90 | ImageSourceFormat srcFormat; 91 | if (HalfChannels.ContainsKey("R") && HalfChannels.ContainsKey("G") && HalfChannels.ContainsKey("B")) { 92 | srcFormat = includeAlpha ? ImageSourceFormat.HalfRGBA : ImageSourceFormat.HalfRGB; 93 | } 94 | else if (FloatChannels.ContainsKey("R") && FloatChannels.ContainsKey("G") && FloatChannels.ContainsKey("B")) { 95 | srcFormat = includeAlpha ? ImageSourceFormat.SingleRGBA : ImageSourceFormat.SingleRGB; 96 | } 97 | else { 98 | throw new EXRFormatException("Unrecognized EXR image format, did not contain half/single RGB color channels"); 99 | } 100 | return GetHalfs(srcFormat, channels, premultiplied, gamma); 101 | } 102 | 103 | public Half[] GetHalfs(ImageSourceFormat srcFormat, ChannelConfiguration channels, bool premultiplied, GammaEncoding gamma) { 104 | ImageDestFormat destFormat; 105 | if (srcFormat == ImageSourceFormat.HalfRGBA || srcFormat == ImageSourceFormat.SingleRGBA) { 106 | if (premultiplied) { 107 | destFormat = 108 | channels == ChannelConfiguration.BGR ? 109 | ImageDestFormat.PremultipliedBGRA16 : 110 | ImageDestFormat.PremultipliedRGBA16; 111 | } 112 | else { 113 | destFormat = 114 | channels == ChannelConfiguration.BGR ? 115 | ImageDestFormat.BGRA16 : 116 | ImageDestFormat.RGBA16; 117 | } 118 | } 119 | else { 120 | destFormat = 121 | channels == ChannelConfiguration.BGR ? 122 | ImageDestFormat.BGR16 : 123 | ImageDestFormat.RGB16; 124 | } 125 | 126 | var bytesPerPixel = EXRFile.GetBytesPerPixel(destFormat); 127 | var channelCount = 128 | srcFormat == ImageSourceFormat.SingleRGB || srcFormat == ImageSourceFormat.HalfRGB ? 3 : 4; 129 | 130 | var bytes = GetBytes(srcFormat, destFormat, gamma, DataWindow.Width * bytesPerPixel); 131 | Half[] halfs = new Half[bytes.Length / 2]; 132 | Buffer.BlockCopy(bytes, 0, halfs, 0, bytes.Length); 133 | return halfs; 134 | } 135 | 136 | /// 137 | /// Gets RGB color channels as an interleaved array of floats. If this EXRPart's HasAlpha property is true, 138 | /// this will include the alpha channel in the array. 139 | /// 140 | public float[] GetFloats(ChannelConfiguration channels, bool premultiplied, GammaEncoding gamma) { 141 | return GetFloats(channels, premultiplied, gamma, HasAlpha); 142 | } 143 | 144 | /// 145 | /// Gets RGB color channels as an interleaved array of floats. If includeAlpha is true, 146 | /// this will include the alpha channel in the array. 147 | /// 148 | public float[] GetFloats(ChannelConfiguration channels, bool premultiplied, GammaEncoding gamma, bool includeAlpha) { 149 | ImageSourceFormat srcFormat; 150 | if (HalfChannels.ContainsKey("R") && HalfChannels.ContainsKey("G") && HalfChannels.ContainsKey("B")) { 151 | srcFormat = includeAlpha ? ImageSourceFormat.HalfRGBA : ImageSourceFormat.HalfRGB; 152 | } 153 | else if (FloatChannels.ContainsKey("R") && FloatChannels.ContainsKey("G") && FloatChannels.ContainsKey("B")) { 154 | srcFormat = includeAlpha ? ImageSourceFormat.SingleRGBA : ImageSourceFormat.SingleRGB; 155 | } 156 | else { 157 | throw new EXRFormatException("Unrecognized EXR image format, did not contain half/single RGB color channels"); 158 | } 159 | return GetFloats(srcFormat, channels, premultiplied, gamma); 160 | } 161 | 162 | public float[] GetFloats(ImageSourceFormat srcFormat, ChannelConfiguration channels, bool premultiplied, GammaEncoding gamma) { 163 | ImageDestFormat destFormat; 164 | if (srcFormat == ImageSourceFormat.HalfRGBA || srcFormat == ImageSourceFormat.SingleRGBA) { 165 | if (premultiplied) { 166 | destFormat = 167 | channels == ChannelConfiguration.BGR ? 168 | ImageDestFormat.PremultipliedBGRA32 : 169 | ImageDestFormat.PremultipliedRGBA32; 170 | } 171 | else { 172 | destFormat = 173 | channels == ChannelConfiguration.BGR ? 174 | ImageDestFormat.BGRA32 : 175 | ImageDestFormat.RGBA32; 176 | } 177 | } 178 | else { 179 | destFormat = 180 | channels == ChannelConfiguration.BGR ? 181 | ImageDestFormat.BGR32 : 182 | ImageDestFormat.RGB32; 183 | } 184 | 185 | var bytesPerPixel = EXRFile.GetBytesPerPixel(destFormat); 186 | var channelCount = 187 | srcFormat == ImageSourceFormat.SingleRGB || srcFormat == ImageSourceFormat.HalfRGB ? 3 : 4; 188 | 189 | var bytes = GetBytes(srcFormat, destFormat, gamma, DataWindow.Width * bytesPerPixel); 190 | float[] floats = new float[bytes.Length / sizeof(float)]; 191 | Buffer.BlockCopy(bytes, 0, floats, 0, bytes.Length); 192 | return floats; 193 | } 194 | 195 | public byte[] GetBytes(ImageDestFormat destFormat, GammaEncoding gamma) { 196 | return GetBytes(destFormat, gamma, DataWindow.Width * EXRFile.GetBytesPerPixel(destFormat)); 197 | } 198 | 199 | public byte[] GetBytes(ImageDestFormat destFormat, GammaEncoding gamma, int stride) { 200 | ImageSourceFormat srcFormat; 201 | if (HalfChannels.ContainsKey("R") && HalfChannels.ContainsKey("G") && HalfChannels.ContainsKey("B")) { 202 | srcFormat = HalfChannels.ContainsKey("A") ? ImageSourceFormat.HalfRGBA : ImageSourceFormat.HalfRGB; 203 | } 204 | else if (FloatChannels.ContainsKey("R") && FloatChannels.ContainsKey("G") && FloatChannels.ContainsKey("B")) { 205 | srcFormat = FloatChannels.ContainsKey("A") ? ImageSourceFormat.SingleRGBA : ImageSourceFormat.SingleRGB; 206 | } 207 | else { 208 | throw new EXRFormatException("Unrecognized EXR image format, did not contain half/single RGB color channels"); 209 | } 210 | return GetBytes(srcFormat, destFormat, gamma, stride); 211 | } 212 | 213 | public byte[] GetBytes(ImageSourceFormat srcFormat, ImageDestFormat destFormat, GammaEncoding gamma) { 214 | return GetBytes(srcFormat, destFormat, gamma, DataWindow.Width * EXRFile.GetBytesPerPixel(destFormat)); 215 | } 216 | 217 | public byte[] GetBytes(ImageSourceFormat srcFormat, ImageDestFormat destFormat, GammaEncoding gamma, int stride) { 218 | CheckHasData(); 219 | 220 | int bytesPerPixel = EXRFile.GetBytesPerPixel(destFormat); 221 | int bitsPerPixel = EXRFile.GetBitsPerPixel(destFormat); 222 | 223 | if (stride < bytesPerPixel * DataWindow.Width) { 224 | throw new ArgumentException("Stride was lower than minimum", "stride"); 225 | } 226 | byte[] buffer = new byte[stride * DataWindow.Height]; 227 | 228 | var padding = stride - bytesPerPixel * DataWindow.Width; 229 | 230 | bool isHalf = srcFormat == ImageSourceFormat.HalfRGB || srcFormat == ImageSourceFormat.HalfRGBA; 231 | bool sourceAlpha = false; 232 | bool destinationAlpha = 233 | destFormat == ImageDestFormat.BGRA16 || 234 | destFormat == ImageDestFormat.BGRA32 || 235 | destFormat == ImageDestFormat.BGRA8 || 236 | destFormat == ImageDestFormat.PremultipliedBGRA16 || 237 | destFormat == ImageDestFormat.PremultipliedBGRA32 || 238 | destFormat == ImageDestFormat.PremultipliedBGRA8 || 239 | destFormat == ImageDestFormat.PremultipliedRGBA16 || 240 | destFormat == ImageDestFormat.PremultipliedRGBA32 || 241 | destFormat == ImageDestFormat.PremultipliedRGBA8 || 242 | destFormat == ImageDestFormat.RGBA16 || 243 | destFormat == ImageDestFormat.RGBA32 || 244 | destFormat == ImageDestFormat.RGBA8; 245 | bool premultiplied = 246 | destFormat == ImageDestFormat.PremultipliedBGRA16 || 247 | destFormat == ImageDestFormat.PremultipliedBGRA32 || 248 | destFormat == ImageDestFormat.PremultipliedBGRA8 || 249 | destFormat == ImageDestFormat.PremultipliedRGBA16 || 250 | destFormat == ImageDestFormat.PremultipliedRGBA32 || 251 | destFormat == ImageDestFormat.PremultipliedRGBA8; 252 | bool bgra = 253 | destFormat == ImageDestFormat.BGR16 || 254 | destFormat == ImageDestFormat.BGR32 || 255 | destFormat == ImageDestFormat.BGR8 || 256 | destFormat == ImageDestFormat.BGRA16 || 257 | destFormat == ImageDestFormat.BGRA32 || 258 | destFormat == ImageDestFormat.BGRA8 || 259 | destFormat == ImageDestFormat.PremultipliedBGRA16 || 260 | destFormat == ImageDestFormat.PremultipliedBGRA32 || 261 | destFormat == ImageDestFormat.PremultipliedBGRA8; 262 | 263 | Half[] hr, hg, hb, ha; 264 | float[] fr, fg, fb, fa; 265 | hr = hg = hb = ha = null; 266 | fr = fg = fb = fa = null; 267 | 268 | if (isHalf) { 269 | if (!HalfChannels.ContainsKey("R")) { 270 | throw new ArgumentException("Half type channel R not found", "srcFormat"); 271 | } 272 | if (!HalfChannels.ContainsKey("G")) { 273 | throw new ArgumentException("Half type channel G not found", "srcFormat"); 274 | } 275 | if (!HalfChannels.ContainsKey("B")) { 276 | throw new ArgumentException("Half type channel B not found", "srcFormat"); 277 | } 278 | hr = HalfChannels["R"]; 279 | hg = HalfChannels["G"]; 280 | hb = HalfChannels["B"]; 281 | 282 | if (srcFormat == ImageSourceFormat.HalfRGBA) { 283 | if (!HalfChannels.ContainsKey("A")) { 284 | throw new ArgumentException("Half type channel A not found", "srcFormat"); 285 | } 286 | ha = HalfChannels["A"]; 287 | sourceAlpha = true; 288 | } 289 | } 290 | else { 291 | if (!FloatChannels.ContainsKey("R")) { 292 | throw new ArgumentException("Single type channel R not found", "srcFormat"); 293 | } 294 | if (!FloatChannels.ContainsKey("G")) { 295 | throw new ArgumentException("Single type channel G not found", "srcFormat"); 296 | } 297 | if (!FloatChannels.ContainsKey("B")) { 298 | throw new ArgumentException("Single type channel B not found", "srcFormat"); 299 | } 300 | fr = FloatChannels["R"]; 301 | fg = FloatChannels["G"]; 302 | fb = FloatChannels["B"]; 303 | 304 | if (srcFormat == ImageSourceFormat.HalfRGBA) { 305 | if (!FloatChannels.ContainsKey("A")) { 306 | throw new ArgumentException("Single type channel A not found", "srcFormat"); 307 | } 308 | fa = FloatChannels["A"]; 309 | sourceAlpha = true; 310 | } 311 | } 312 | 313 | #if !PARALLEL 314 | int srcIndex = 0; 315 | int destIndex = 0; 316 | 317 | BinaryWriter writer = new BinaryWriter(new MemoryStream(buffer)); 318 | 319 | for (int y = 0; y < DataWindow.Height; y++, destIndex += padding) { 320 | GetScanlineBytes(bytesPerPixel, destIndex, srcIndex, isHalf, destinationAlpha, sourceAlpha, 321 | hr, hg, hb, ha, fr, fg, fb, fa, 322 | bitsPerPixel, gamma, premultiplied, bgra, buffer, writer); 323 | destIndex += DataWindow.Width * bytesPerPixel; 324 | srcIndex += DataWindow.Width; 325 | } 326 | 327 | writer.Dispose(); 328 | writer.BaseStream.Dispose(); 329 | #else 330 | var actions = (from y in Enumerable.Range(0, DataWindow.Height) select (Action)(() => { 331 | var destIndex = stride * y; 332 | var srcIndex = DataWindow.Width * y; 333 | 334 | using (var stream = new MemoryStream(buffer)) { 335 | using (var writer = new BinaryWriter(stream)) { 336 | GetScanlineBytes(bytesPerPixel, destIndex, srcIndex, isHalf, destinationAlpha, sourceAlpha, 337 | hr, hg, hb, ha, fr, fg, fb, fa, 338 | bitsPerPixel, gamma, premultiplied, bgra, buffer, writer); 339 | } 340 | } 341 | })).ToArray(); 342 | Parallel.Invoke(actions); 343 | #endif 344 | 345 | return buffer; 346 | } 347 | 348 | private void GetScanlineBytes( 349 | int bytesPerPixel, int destIndex, int srcIndex, bool isHalf, bool destinationAlpha, bool sourceAlpha, 350 | Half[] hr, Half[] hg, Half[] hb, Half[] ha, float[] fr, float[] fg, float[] fb, float[] fa, 351 | int bitsPerPixel, GammaEncoding gamma, bool premultiplied, bool bgra, byte[] buffer, BinaryWriter writer 352 | ) { 353 | writer.Seek(destIndex, SeekOrigin.Begin); 354 | for (int x = 0; x < DataWindow.Width; x++, destIndex += bytesPerPixel, srcIndex++) { 355 | float r, g, b, a; 356 | 357 | // get source channels as floats 358 | if (isHalf) { 359 | r = hr[srcIndex]; 360 | g = hg[srcIndex]; 361 | b = hb[srcIndex]; 362 | 363 | if (destinationAlpha) { 364 | a = sourceAlpha ? (float)ha[srcIndex] : 1.0f; 365 | } 366 | else { 367 | a = 1.0f; 368 | } 369 | } 370 | else { 371 | r = fr[srcIndex]; 372 | g = fg[srcIndex]; 373 | b = fb[srcIndex]; 374 | 375 | if (destinationAlpha) { 376 | a = sourceAlpha ? fa[srcIndex] : 1.0f; 377 | } 378 | else { 379 | a = 1.0f; 380 | } 381 | } 382 | 383 | // convert to destination format 384 | if (bitsPerPixel == 8) { 385 | byte r8, g8, b8, a8 = 255; 386 | 387 | if (gamma == GammaEncoding.Linear) { 388 | if (premultiplied) { 389 | r8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(r * a * 255 + 0.5))); 390 | g8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(g * a * 255 + 0.5))); 391 | b8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(b * a * 255 + 0.5))); 392 | a8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(a * 255 + 0.5))); 393 | } 394 | else { 395 | r8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(r * 255 + 0.5))); 396 | g8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(g * 255 + 0.5))); 397 | b8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(b * 255 + 0.5))); 398 | if (destinationAlpha) { 399 | a8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(a * 255 + 0.5))); 400 | } 401 | } 402 | } 403 | else if (gamma == GammaEncoding.Gamma) { 404 | if (premultiplied) { 405 | r8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress(r) * a * 255 + 0.5))); 406 | g8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress(g) * a * 255 + 0.5))); 407 | b8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress(b) * a * 255 + 0.5))); 408 | a8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(a * 255 + 0.5))); 409 | } 410 | else { 411 | r8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress(r) * 255 + 0.5))); 412 | g8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress(g) * 255 + 0.5))); 413 | b8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress(b) * 255 + 0.5))); 414 | if (destinationAlpha) { 415 | a8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(a * 255 + 0.5))); 416 | } 417 | } 418 | } 419 | else { // sRGB 420 | if (premultiplied) { 421 | r8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress_sRGB(r) * a * 255 + 0.5))); 422 | g8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress_sRGB(g) * a * 255 + 0.5))); 423 | b8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress_sRGB(b) * a * 255 + 0.5))); 424 | a8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(a * 255 + 0.5))); 425 | } 426 | else { 427 | r8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress_sRGB(r) * 255 + 0.5))); 428 | g8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress_sRGB(g) * 255 + 0.5))); 429 | b8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(Gamma.Compress_sRGB(b) * 255 + 0.5))); 430 | if (destinationAlpha) { 431 | a8 = (byte)Math.Min(255, Math.Max(0, Math.Floor(a * 255 + 0.5))); 432 | } 433 | } 434 | } 435 | 436 | if (bgra) { 437 | buffer[destIndex] = b8; 438 | buffer[destIndex + 1] = g8; 439 | buffer[destIndex + 2] = r8; 440 | } 441 | else { 442 | buffer[destIndex] = r8; 443 | buffer[destIndex + 1] = g8; 444 | buffer[destIndex + 2] = b8; 445 | } 446 | if (destinationAlpha) { 447 | buffer[destIndex + 3] = a8; 448 | } 449 | } 450 | else if (bitsPerPixel == 32) { 451 | float r32, g32, b32, a32 = 1.0f; 452 | 453 | if (gamma == GammaEncoding.Linear) { 454 | if (premultiplied) { 455 | r32 = r * a; 456 | g32 = g * a; 457 | b32 = b * a; 458 | a32 = a; 459 | } 460 | else { 461 | r32 = r; 462 | g32 = g; 463 | b32 = b; 464 | if (destinationAlpha) { 465 | a32 = a; 466 | } 467 | } 468 | } 469 | else if (gamma == GammaEncoding.Gamma) { 470 | if (premultiplied) { 471 | r32 = Gamma.Compress(r) * a; 472 | g32 = Gamma.Compress(g) * a; 473 | b32 = Gamma.Compress(b) * a; 474 | a32 = a; 475 | } 476 | else { 477 | r32 = Gamma.Compress(r); 478 | g32 = Gamma.Compress(g); 479 | b32 = Gamma.Compress(b); 480 | if (destinationAlpha) { 481 | a32 = a; 482 | } 483 | } 484 | } 485 | else { // sRGB 486 | if (premultiplied) { 487 | r32 = Gamma.Compress_sRGB(r) * a; 488 | g32 = Gamma.Compress_sRGB(g) * a; 489 | b32 = Gamma.Compress_sRGB(b) * a; 490 | a32 = a; 491 | } 492 | else { 493 | r32 = Gamma.Compress_sRGB(r); 494 | g32 = Gamma.Compress_sRGB(g); 495 | b32 = Gamma.Compress_sRGB(b); 496 | if (destinationAlpha) { 497 | a32 = a; 498 | } 499 | } 500 | } 501 | 502 | if (bgra) { 503 | writer.Write(b32); 504 | writer.Write(g32); 505 | writer.Write(r32); 506 | } 507 | else { 508 | writer.Write(r32); 509 | writer.Write(g32); 510 | writer.Write(b32); 511 | } 512 | if (destinationAlpha) { 513 | writer.Write(a32); 514 | } 515 | } 516 | else { // 16 517 | Half r16, g16, b16, a16 = new Half(1.0f); 518 | 519 | if (gamma == GammaEncoding.Linear) { 520 | if (premultiplied) { 521 | r16 = (Half)(r * a); 522 | g16 = (Half)(g * a); 523 | b16 = (Half)(b * a); 524 | a16 = (Half)a; 525 | } 526 | else { 527 | r16 = (Half)r; 528 | g16 = (Half)g; 529 | b16 = (Half)b; 530 | if (destinationAlpha) { 531 | a16 = (Half)a; 532 | } 533 | } 534 | } 535 | else if (gamma == GammaEncoding.Gamma) { 536 | if (premultiplied) { 537 | r16 = (Half)(Gamma.Compress(r) * a); 538 | g16 = (Half)(Gamma.Compress(g) * a); 539 | b16 = (Half)(Gamma.Compress(b) * a); 540 | a16 = (Half)a; 541 | } 542 | else { 543 | r16 = (Half)Gamma.Compress(r); 544 | g16 = (Half)Gamma.Compress(g); 545 | b16 = (Half)Gamma.Compress(b); 546 | if (destinationAlpha) { 547 | a16 = (Half)a; 548 | } 549 | } 550 | } 551 | else { // sRGB 552 | if (premultiplied) { 553 | r16 = (Half)(Gamma.Compress_sRGB(r) * a); 554 | g16 = (Half)(Gamma.Compress_sRGB(g) * a); 555 | b16 = (Half)(Gamma.Compress_sRGB(b) * a); 556 | a16 = (Half)a; 557 | } 558 | else { 559 | r16 = (Half)Gamma.Compress_sRGB(r); 560 | g16 = (Half)Gamma.Compress_sRGB(g); 561 | b16 = (Half)Gamma.Compress_sRGB(b); 562 | if (destinationAlpha) { 563 | a16 = (Half)a; 564 | } 565 | } 566 | } 567 | 568 | if (bgra) { 569 | writer.Write(b16.value); 570 | writer.Write(g16.value); 571 | writer.Write(r16.value); 572 | } 573 | else { 574 | writer.Write(r16.value); 575 | writer.Write(g16.value); 576 | writer.Write(b16.value); 577 | } 578 | if (destinationAlpha) { 579 | writer.Write(a16.value); 580 | } 581 | } 582 | } 583 | } 584 | 585 | #if DOTNET 586 | public void Open(string file) { 587 | var reader = new EXRReader(new FileStream(file, FileMode.Open, FileAccess.Read)); 588 | Open(reader); 589 | reader.Dispose(); 590 | } 591 | #endif 592 | 593 | public void Open(Stream stream) { 594 | var reader = new EXRReader(new BinaryReader(stream)); 595 | Open(reader); 596 | reader.Dispose(); 597 | } 598 | 599 | public void Close() { 600 | hasData = false; 601 | HalfChannels.Clear(); 602 | FloatChannels.Clear(); 603 | } 604 | 605 | public void Open(IEXRReader reader) { 606 | hasData = true; 607 | ReadPixelData(reader); 608 | } 609 | 610 | private void ReadPixelBlock(IEXRReader reader, uint offset, int linesPerBlock, List sortedChannels) { 611 | reader.Position = (int)offset; 612 | 613 | if (Version.IsMultiPart) { 614 | // we don't use this. should we? i dunno. probably not 615 | reader.ReadUInt32(); reader.ReadUInt32(); 616 | } 617 | 618 | var startY = reader.ReadInt32(); 619 | var endY = Math.Min(DataWindow.Height, startY + linesPerBlock); 620 | var startIndex = startY * DataWindow.Width; 621 | 622 | var dataSize = reader.ReadInt32(); 623 | 624 | if (Header.Compression != EXRCompression.None) { 625 | throw new NotImplementedException("Compressed images are currently not supported"); 626 | } 627 | 628 | foreach (var channel in sortedChannels) { 629 | float[] floatArr = null; 630 | Half[] halfArr = null; 631 | 632 | if (channel.Type == PixelType.Float) { 633 | floatArr = FloatChannels[channel.Name]; 634 | } 635 | else if (channel.Type == PixelType.Half) { 636 | halfArr = HalfChannels[channel.Name]; 637 | } 638 | else { 639 | throw new NotImplementedException(); 640 | } 641 | 642 | var index = startIndex; 643 | for (int y = startY; y < endY; y++) { 644 | for (int x = 0; x < DataWindow.Width; x++, index++) { 645 | if (channel.Type == PixelType.Float) { 646 | floatArr[index] = reader.ReadSingle(); 647 | } 648 | else if (channel.Type == PixelType.Half) { 649 | halfArr[index] = reader.ReadHalf(); 650 | } 651 | else { 652 | throw new NotImplementedException(); 653 | } 654 | } 655 | } 656 | } 657 | } 658 | 659 | #if PARALLEL 660 | public void OpenParallel(string file) { 661 | OpenParallel(() => { 662 | return new EXRReader(new FileStream(file, FileMode.Open, FileAccess.Read)); 663 | }); 664 | } 665 | 666 | public void OpenParallel(ParallelReaderCreationDelegate createReader) { 667 | hasData = true; 668 | ReadPixelDataParallel(createReader); 669 | } 670 | 671 | private void ReadPixelDataParallel(ParallelReaderCreationDelegate createReader) { 672 | var linesPerBlock = EXRFile.GetScanLinesPerBlock(Header.Compression); 673 | var sortedChannels = (from c in Header.Channels orderby c.Name select c).ToList(); 674 | 675 | var actions = (from offset in Offsets select (Action)(() => { 676 | var reader = createReader(); 677 | ReadPixelBlock(reader, offset, linesPerBlock, sortedChannels); 678 | reader.Dispose(); 679 | })); 680 | Parallel.Invoke(actions.ToArray()); 681 | } 682 | #else 683 | public void OpenParallel(string file) { 684 | Open(file); 685 | } 686 | 687 | public void OpenParallel(ParallelReaderCreationDelegate createReader) { 688 | var reader = createReader(); 689 | Open(reader); 690 | reader.Dispose(); 691 | } 692 | #endif 693 | 694 | protected void ReadPixelData(IEXRReader reader) { 695 | var linesPerBlock = EXRFile.GetScanLinesPerBlock(Header.Compression); 696 | var sortedChannels = (from c in Header.Channels orderby c.Name select c).ToList(); 697 | 698 | //var actions = (from offset in Offsets select (Action)(() => { 699 | //})); 700 | //Parallel.Invoke(actions.ToArray()); 701 | foreach (var offset in Offsets) { 702 | ReadPixelBlock(reader, offset, linesPerBlock, sortedChannels); 703 | } 704 | } 705 | 706 | public bool IsRGB { 707 | get { 708 | return 709 | (HalfChannels.ContainsKey("R") || FloatChannels.ContainsKey("R")) && 710 | (HalfChannels.ContainsKey("G") || FloatChannels.ContainsKey("G")) && 711 | (HalfChannels.ContainsKey("B") || FloatChannels.ContainsKey("B")); 712 | } 713 | } 714 | 715 | public bool HasAlpha { 716 | get { 717 | return HalfChannels.ContainsKey("A") || FloatChannels.ContainsKey("A"); 718 | } 719 | } 720 | } 721 | } 722 | -------------------------------------------------------------------------------- /SharpEXR/Half.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace SharpEXR { 7 | /// 8 | /// Represents a half-precision floating point number. 9 | /// 10 | /// 11 | /// Note: 12 | /// Half is not fast enought and precision is also very bad, 13 | /// so is should not be used for matemathical computation (use Single instead). 14 | /// The main advantage of Half type is lower memory cost: two bytes per number. 15 | /// Half is typically used in graphical applications. 16 | /// 17 | /// Note: 18 | /// All functions, where is used conversion half->float/float->half, 19 | /// are approx. ten times slower than float->double/double->float, i.e. ~3ns on 2GHz CPU. 20 | /// 21 | /// References: 22 | /// - Fast Half Float Conversions, Jeroen van der Zijp, link: http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf 23 | /// - IEEE 754 revision, link: http://grouper.ieee.org/groups/754/ 24 | /// 25 | [Serializable] 26 | public struct Half : IComparable, IFormattable, IConvertible, IComparable, IEquatable { 27 | /// 28 | /// Internal representation of the half-precision floating-point number. 29 | /// 30 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 31 | internal ushort value; 32 | 33 | #region Constants 34 | /// 35 | /// Represents the smallest positive System.Half value greater than zero. This field is constant. 36 | /// 37 | public static readonly Half Epsilon = Half.ToHalf(0x0001); 38 | /// 39 | /// Represents the largest possible value of System.Half. This field is constant. 40 | /// 41 | public static readonly Half MaxValue = Half.ToHalf(0x7bff); 42 | /// 43 | /// Represents the smallest possible value of System.Half. This field is constant. 44 | /// 45 | public static readonly Half MinValue = Half.ToHalf(0xfbff); 46 | /// 47 | /// Represents not a number (NaN). This field is constant. 48 | /// 49 | public static readonly Half NaN = Half.ToHalf(0xfe00); 50 | /// 51 | /// Represents negative infinity. This field is constant. 52 | /// 53 | public static readonly Half NegativeInfinity = Half.ToHalf(0xfc00); 54 | /// 55 | /// Represents positive infinity. This field is constant. 56 | /// 57 | public static readonly Half PositiveInfinity = Half.ToHalf(0x7c00); 58 | #endregion 59 | 60 | #region Constructors 61 | /// 62 | /// Initializes a new instance of System.Half to the value of the specified single-precision floating-point number. 63 | /// 64 | /// The value to represent as a System.Half. 65 | public Half(float value) { this = HalfHelper.SingleToHalf(value); } 66 | /// 67 | /// Initializes a new instance of System.Half to the value of the specified 32-bit signed integer. 68 | /// 69 | /// The value to represent as a System.Half. 70 | public Half(int value) : this((float)value) { } 71 | /// 72 | /// Initializes a new instance of System.Half to the value of the specified 64-bit signed integer. 73 | /// 74 | /// The value to represent as a System.Half. 75 | public Half(long value) : this((float)value) { } 76 | /// 77 | /// Initializes a new instance of System.Half to the value of the specified double-precision floating-point number. 78 | /// 79 | /// The value to represent as a System.Half. 80 | public Half(double value) : this((float)value) { } 81 | /// 82 | /// Initializes a new instance of System.Half to the value of the specified decimal number. 83 | /// 84 | /// The value to represent as a System.Half. 85 | public Half(decimal value) : this((float)value) { } 86 | /// 87 | /// Initializes a new instance of System.Half to the value of the specified 32-bit unsigned integer. 88 | /// 89 | /// The value to represent as a System.Half. 90 | public Half(uint value) : this((float)value) { } 91 | /// 92 | /// Initializes a new instance of System.Half to the value of the specified 64-bit unsigned integer. 93 | /// 94 | /// The value to represent as a System.Half. 95 | public Half(ulong value) : this((float)value) { } 96 | #endregion 97 | 98 | #region Numeric operators 99 | 100 | /// 101 | /// Returns the result of multiplying the specified System.Half value by negative one. 102 | /// 103 | /// A System.Half. 104 | /// A System.Half with the value of half, but the opposite sign. -or- Zero, if half is zero. 105 | public static Half Negate(Half half) { return -half; } 106 | /// 107 | /// Adds two specified System.Half values. 108 | /// 109 | /// A System.Half. 110 | /// A System.Half. 111 | /// A System.Half value that is the sum of half1 and half2. 112 | public static Half Add(Half half1, Half half2) { return half1 + half2; } 113 | /// 114 | /// Subtracts one specified System.Half value from another. 115 | /// 116 | /// A System.Half (the minuend). 117 | /// A System.Half (the subtrahend). 118 | /// The System.Half result of subtracting half2 from half1. 119 | public static Half Subtract(Half half1, Half half2) { return half1 - half2; } 120 | /// 121 | /// Multiplies two specified System.Half values. 122 | /// 123 | /// A System.Half (the multiplicand). 124 | /// A System.Half (the multiplier). 125 | /// A System.Half that is the result of multiplying half1 and half2. 126 | public static Half Multiply(Half half1, Half half2) { return half1 * half2; } 127 | /// 128 | /// Divides two specified System.Half values. 129 | /// 130 | /// A System.Half (the dividend). 131 | /// A System.Half (the divisor). 132 | /// The System.Half that is the result of dividing half1 by half2. 133 | /// half2 is zero. 134 | public static Half Divide(Half half1, Half half2) { return half1 / half2; } 135 | 136 | /// 137 | /// Returns the value of the System.Half operand (the sign of the operand is unchanged). 138 | /// 139 | /// The System.Half operand. 140 | /// The value of the operand, half. 141 | public static Half operator +(Half half) { return half; } 142 | /// 143 | /// Negates the value of the specified System.Half operand. 144 | /// 145 | /// The System.Half operand. 146 | /// The result of half multiplied by negative one (-1). 147 | public static Half operator -(Half half) { return HalfHelper.Negate(half); } 148 | /// 149 | /// Increments the System.Half operand by 1. 150 | /// 151 | /// The System.Half operand. 152 | /// The value of half incremented by 1. 153 | public static Half operator ++(Half half) { return (Half)(half + 1f); } 154 | /// 155 | /// Decrements the System.Half operand by one. 156 | /// 157 | /// The System.Half operand. 158 | /// The value of half decremented by 1. 159 | public static Half operator --(Half half) { return (Half)(half - 1f); } 160 | /// 161 | /// Adds two specified System.Half values. 162 | /// 163 | /// A System.Half. 164 | /// A System.Half. 165 | /// The System.Half result of adding half1 and half2. 166 | public static Half operator +(Half half1, Half half2) { return (Half)((float)half1 + (float)half2); } 167 | /// 168 | /// Subtracts two specified System.Half values. 169 | /// 170 | /// A System.Half. 171 | /// A System.Half. 172 | /// The System.Half result of subtracting half1 and half2. 173 | public static Half operator -(Half half1, Half half2) { return (Half)((float)half1 - (float)half2); } 174 | /// 175 | /// Multiplies two specified System.Half values. 176 | /// 177 | /// A System.Half. 178 | /// A System.Half. 179 | /// The System.Half result of multiplying half1 by half2. 180 | public static Half operator *(Half half1, Half half2) { return (Half)((float)half1 * (float)half2); } 181 | /// 182 | /// Divides two specified System.Half values. 183 | /// 184 | /// A System.Half (the dividend). 185 | /// A System.Half (the divisor). 186 | /// The System.Half result of half1 by half2. 187 | public static Half operator /(Half half1, Half half2) { return (Half)((float)half1 / (float)half2); } 188 | /// 189 | /// Returns a value indicating whether two instances of System.Half are equal. 190 | /// 191 | /// A System.Half. 192 | /// A System.Half. 193 | /// true if half1 and half2 are equal; otherwise, false. 194 | public static bool operator ==(Half half1, Half half2) { return (!IsNaN(half1) && (half1.value == half2.value)); } 195 | /// 196 | /// Returns a value indicating whether two instances of System.Half are not equal. 197 | /// 198 | /// A System.Half. 199 | /// A System.Half. 200 | /// true if half1 and half2 are not equal; otherwise, false. 201 | public static bool operator !=(Half half1, Half half2) { return !(half1.value == half2.value); } 202 | /// 203 | /// Returns a value indicating whether a specified System.Half is less than another specified System.Half. 204 | /// 205 | /// A System.Half. 206 | /// A System.Half. 207 | /// true if half1 is less than half1; otherwise, false. 208 | public static bool operator <(Half half1, Half half2) { return (float)half1 < (float)half2; } 209 | /// 210 | /// Returns a value indicating whether a specified System.Half is greater than another specified System.Half. 211 | /// 212 | /// A System.Half. 213 | /// A System.Half. 214 | /// true if half1 is greater than half2; otherwise, false. 215 | public static bool operator >(Half half1, Half half2) { return (float)half1 > (float)half2; } 216 | /// 217 | /// Returns a value indicating whether a specified System.Half is less than or equal to another specified System.Half. 218 | /// 219 | /// A System.Half. 220 | /// A System.Half. 221 | /// true if half1 is less than or equal to half2; otherwise, false. 222 | public static bool operator <=(Half half1, Half half2) { return (half1 == half2) || (half1 < half2); } 223 | /// 224 | /// Returns a value indicating whether a specified System.Half is greater than or equal to another specified System.Half. 225 | /// 226 | /// A System.Half. 227 | /// A System.Half. 228 | /// true if half1 is greater than or equal to half2; otherwise, false. 229 | public static bool operator >=(Half half1, Half half2) { return (half1 == half2) || (half1 > half2); } 230 | #endregion 231 | 232 | #region Type casting operators 233 | /// 234 | /// Converts an 8-bit unsigned integer to a System.Half. 235 | /// 236 | /// An 8-bit unsigned integer. 237 | /// A System.Half that represents the converted 8-bit unsigned integer. 238 | public static implicit operator Half(byte value) { return new Half((float)value); } 239 | /// 240 | /// Converts a 16-bit signed integer to a System.Half. 241 | /// 242 | /// A 16-bit signed integer. 243 | /// A System.Half that represents the converted 16-bit signed integer. 244 | public static implicit operator Half(short value) { return new Half((float)value); } 245 | /// 246 | /// Converts a Unicode character to a System.Half. 247 | /// 248 | /// A Unicode character. 249 | /// A System.Half that represents the converted Unicode character. 250 | public static implicit operator Half(char value) { return new Half((float)value); } 251 | /// 252 | /// Converts a 32-bit signed integer to a System.Half. 253 | /// 254 | /// A 32-bit signed integer. 255 | /// A System.Half that represents the converted 32-bit signed integer. 256 | public static implicit operator Half(int value) { return new Half((float)value); } 257 | /// 258 | /// Converts a 64-bit signed integer to a System.Half. 259 | /// 260 | /// A 64-bit signed integer. 261 | /// A System.Half that represents the converted 64-bit signed integer. 262 | public static implicit operator Half(long value) { return new Half((float)value); } 263 | /// 264 | /// Converts a single-precision floating-point number to a System.Half. 265 | /// 266 | /// A single-precision floating-point number. 267 | /// A System.Half that represents the converted single-precision floating point number. 268 | public static explicit operator Half(float value) { return new Half((float)value); } 269 | /// 270 | /// Converts a double-precision floating-point number to a System.Half. 271 | /// 272 | /// A double-precision floating-point number. 273 | /// A System.Half that represents the converted double-precision floating point number. 274 | public static explicit operator Half(double value) { return new Half((float)value); } 275 | /// 276 | /// Converts a decimal number to a System.Half. 277 | /// 278 | /// decimal number 279 | /// A System.Half that represents the converted decimal number. 280 | public static explicit operator Half(decimal value) { return new Half((float)value); } 281 | /// 282 | /// Converts a System.Half to an 8-bit unsigned integer. 283 | /// 284 | /// A System.Half to convert. 285 | /// An 8-bit unsigned integer that represents the converted System.Half. 286 | public static explicit operator byte(Half value) { return (byte)(float)value; } 287 | /// 288 | /// Converts a System.Half to a Unicode character. 289 | /// 290 | /// A System.Half to convert. 291 | /// A Unicode character that represents the converted System.Half. 292 | public static explicit operator char(Half value) { return (char)(float)value; } 293 | /// 294 | /// Converts a System.Half to a 16-bit signed integer. 295 | /// 296 | /// A System.Half to convert. 297 | /// A 16-bit signed integer that represents the converted System.Half. 298 | public static explicit operator short(Half value) { return (short)(float)value; } 299 | /// 300 | /// Converts a System.Half to a 32-bit signed integer. 301 | /// 302 | /// A System.Half to convert. 303 | /// A 32-bit signed integer that represents the converted System.Half. 304 | public static explicit operator int(Half value) { return (int)(float)value; } 305 | /// 306 | /// Converts a System.Half to a 64-bit signed integer. 307 | /// 308 | /// A System.Half to convert. 309 | /// A 64-bit signed integer that represents the converted System.Half. 310 | public static explicit operator long(Half value) { return (long)(float)value; } 311 | /// 312 | /// Converts a System.Half to a single-precision floating-point number. 313 | /// 314 | /// A System.Half to convert. 315 | /// A single-precision floating-point number that represents the converted System.Half. 316 | public static implicit operator float(Half value) { return (float)HalfHelper.HalfToSingle(value); } 317 | /// 318 | /// Converts a System.Half to a double-precision floating-point number. 319 | /// 320 | /// A System.Half to convert. 321 | /// A double-precision floating-point number that represents the converted System.Half. 322 | public static implicit operator double(Half value) { return (double)(float)value; } 323 | /// 324 | /// Converts a System.Half to a decimal number. 325 | /// 326 | /// A System.Half to convert. 327 | /// A decimal number that represents the converted System.Half. 328 | public static explicit operator decimal(Half value) { return (decimal)(float)value; } 329 | /// 330 | /// Converts an 8-bit signed integer to a System.Half. 331 | /// 332 | /// An 8-bit signed integer. 333 | /// A System.Half that represents the converted 8-bit signed integer. 334 | public static implicit operator Half(sbyte value) { return new Half((float)value); } 335 | /// 336 | /// Converts a 16-bit unsigned integer to a System.Half. 337 | /// 338 | /// A 16-bit unsigned integer. 339 | /// A System.Half that represents the converted 16-bit unsigned integer. 340 | public static implicit operator Half(ushort value) { return new Half((float)value); } 341 | /// 342 | /// Converts a 32-bit unsigned integer to a System.Half. 343 | /// 344 | /// A 32-bit unsigned integer. 345 | /// A System.Half that represents the converted 32-bit unsigned integer. 346 | public static implicit operator Half(uint value) { return new Half((float)value); } 347 | /// 348 | /// Converts a 64-bit unsigned integer to a System.Half. 349 | /// 350 | /// A 64-bit unsigned integer. 351 | /// A System.Half that represents the converted 64-bit unsigned integer. 352 | public static implicit operator Half(ulong value) { return new Half((float)value); } 353 | /// 354 | /// Converts a System.Half to an 8-bit signed integer. 355 | /// 356 | /// A System.Half to convert. 357 | /// An 8-bit signed integer that represents the converted System.Half. 358 | public static explicit operator sbyte(Half value) { return (sbyte)(float)value; } 359 | /// 360 | /// Converts a System.Half to a 16-bit unsigned integer. 361 | /// 362 | /// A System.Half to convert. 363 | /// A 16-bit unsigned integer that represents the converted System.Half. 364 | public static explicit operator ushort(Half value) { return (ushort)(float)value; } 365 | /// 366 | /// Converts a System.Half to a 32-bit unsigned integer. 367 | /// 368 | /// A System.Half to convert. 369 | /// A 32-bit unsigned integer that represents the converted System.Half. 370 | public static explicit operator uint(Half value) { return (uint)(float)value; } 371 | /// 372 | /// Converts a System.Half to a 64-bit unsigned integer. 373 | /// 374 | /// A System.Half to convert. 375 | /// A 64-bit unsigned integer that represents the converted System.Half. 376 | public static explicit operator ulong(Half value) { return (ulong)(float)value; } 377 | #endregion 378 | 379 | /// 380 | /// Compares this instance to a specified System.Half object. 381 | /// 382 | /// A System.Half object. 383 | /// 384 | /// A signed number indicating the relative values of this instance and value. 385 | /// Return Value Meaning Less than zero This instance is less than value. Zero 386 | /// This instance is equal to value. Greater than zero This instance is greater than value. 387 | /// 388 | public int CompareTo(Half other) { 389 | int result = 0; 390 | if (this < other) { 391 | result = -1; 392 | } 393 | else if (this > other) { 394 | result = 1; 395 | } 396 | else if (this != other) { 397 | if (!IsNaN(this)) { 398 | result = 1; 399 | } 400 | else if (!IsNaN(other)) { 401 | result = -1; 402 | } 403 | } 404 | 405 | return result; 406 | } 407 | /// 408 | /// Compares this instance to a specified System.Object. 409 | /// 410 | /// An System.Object or null. 411 | /// 412 | /// A signed number indicating the relative values of this instance and value. 413 | /// Return Value Meaning Less than zero This instance is less than value. Zero 414 | /// This instance is equal to value. Greater than zero This instance is greater 415 | /// than value. -or- value is null. 416 | /// 417 | /// value is not a System.Half 418 | public int CompareTo(object obj) { 419 | int result = 0; 420 | if (obj == null) { 421 | result = 1; 422 | } 423 | else { 424 | if (obj is Half) { 425 | result = CompareTo((Half)obj); 426 | } 427 | else { 428 | throw new ArgumentException("Object must be of type Half."); 429 | } 430 | } 431 | 432 | return result; 433 | } 434 | /// 435 | /// Returns a value indicating whether this instance and a specified System.Half object represent the same value. 436 | /// 437 | /// A System.Half object to compare to this instance. 438 | /// true if value is equal to this instance; otherwise, false. 439 | public bool Equals(Half other) { 440 | return ((other == this) || (IsNaN(other) && IsNaN(this))); 441 | } 442 | /// 443 | /// Returns a value indicating whether this instance and a specified System.Object 444 | /// represent the same type and value. 445 | /// 446 | /// An System.Object. 447 | /// true if value is a System.Half and equal to this instance; otherwise, false. 448 | public override bool Equals(object obj) { 449 | bool result = false; 450 | if (obj is Half) { 451 | Half half = (Half)obj; 452 | if ((half == this) || (IsNaN(half) && IsNaN(this))) { 453 | result = true; 454 | } 455 | } 456 | 457 | return result; 458 | } 459 | /// 460 | /// Returns the hash code for this instance. 461 | /// 462 | /// A 32-bit signed integer hash code. 463 | public override int GetHashCode() { 464 | return value.GetHashCode(); 465 | } 466 | /// 467 | /// Returns the System.TypeCode for value type System.Half. 468 | /// 469 | /// The enumerated constant (TypeCode)255. 470 | public TypeCode GetTypeCode() { 471 | return (TypeCode)255; 472 | } 473 | 474 | #region BitConverter & Math methods for Half 475 | /// 476 | /// Returns the specified half-precision floating point value as an array of bytes. 477 | /// 478 | /// The number to convert. 479 | /// An array of bytes with length 2. 480 | public static byte[] GetBytes(Half value) { 481 | return BitConverter.GetBytes(value.value); 482 | } 483 | /// 484 | /// Converts the value of a specified instance of System.Half to its equivalent binary representation. 485 | /// 486 | /// A System.Half value. 487 | /// A 16-bit unsigned integer that contain the binary representation of value. 488 | public static ushort GetBits(Half value) { 489 | return value.value; 490 | } 491 | /// 492 | /// Returns a half-precision floating point number converted from two bytes 493 | /// at a specified position in a byte array. 494 | /// 495 | /// An array of bytes. 496 | /// The starting position within value. 497 | /// A half-precision floating point number formed by two bytes beginning at startIndex. 498 | /// 499 | /// startIndex is greater than or equal to the length of value minus 1, and is 500 | /// less than or equal to the length of value minus 1. 501 | /// 502 | /// value is null. 503 | /// startIndex is less than zero or greater than the length of value minus 1. 504 | public static Half ToHalf(byte[] value, int startIndex) { 505 | return Half.ToHalf((ushort)BitConverter.ToInt16(value, startIndex)); 506 | } 507 | /// 508 | /// Returns a half-precision floating point number converted from its binary representation. 509 | /// 510 | /// Binary representation of System.Half value 511 | /// A half-precision floating point number formed by its binary representation. 512 | public static Half ToHalf(ushort bits) { 513 | return new Half { value = bits }; 514 | } 515 | 516 | /// 517 | /// Returns a value indicating the sign of a half-precision floating-point number. 518 | /// 519 | /// A signed number. 520 | /// 521 | /// A number indicating the sign of value. Number Description -1 value is less 522 | /// than zero. 0 value is equal to zero. 1 value is greater than zero. 523 | /// 524 | /// value is equal to System.Half.NaN. 525 | public static int Sign(Half value) { 526 | if (value < 0) { 527 | return -1; 528 | } 529 | else if (value > 0) { 530 | return 1; 531 | } 532 | else { 533 | if (value != 0) { 534 | throw new ArithmeticException("Function does not accept floating point Not-a-Number values."); 535 | } 536 | } 537 | 538 | return 0; 539 | } 540 | /// 541 | /// Returns the absolute value of a half-precision floating-point number. 542 | /// 543 | /// A number in the range System.Half.MinValue ≤ value ≤ System.Half.MaxValue. 544 | /// A half-precision floating-point number, x, such that 0 ≤ x ≤System.Half.MaxValue. 545 | public static Half Abs(Half value) { 546 | return HalfHelper.Abs(value); 547 | } 548 | /// 549 | /// Returns the larger of two half-precision floating-point numbers. 550 | /// 551 | /// The first of two half-precision floating-point numbers to compare. 552 | /// The second of two half-precision floating-point numbers to compare. 553 | /// 554 | /// Parameter value1 or value2, whichever is larger. If value1, or value2, or both val1 555 | /// and value2 are equal to System.Half.NaN, System.Half.NaN is returned. 556 | /// 557 | public static Half Max(Half value1, Half value2) { 558 | return (value1 < value2) ? value2 : value1; 559 | } 560 | /// 561 | /// Returns the smaller of two half-precision floating-point numbers. 562 | /// 563 | /// The first of two half-precision floating-point numbers to compare. 564 | /// The second of two half-precision floating-point numbers to compare. 565 | /// 566 | /// Parameter value1 or value2, whichever is smaller. If value1, or value2, or both val1 567 | /// and value2 are equal to System.Half.NaN, System.Half.NaN is returned. 568 | /// 569 | public static Half Min(Half value1, Half value2) { 570 | return (value1 < value2) ? value1 : value2; 571 | } 572 | #endregion 573 | 574 | /// 575 | /// Returns a value indicating whether the specified number evaluates to not a number (System.Half.NaN). 576 | /// 577 | /// A half-precision floating-point number. 578 | /// true if value evaluates to not a number (System.Half.NaN); otherwise, false. 579 | public static bool IsNaN(Half half) { 580 | return HalfHelper.IsNaN(half); 581 | } 582 | /// 583 | /// Returns a value indicating whether the specified number evaluates to negative or positive infinity. 584 | /// 585 | /// A half-precision floating-point number. 586 | /// true if half evaluates to System.Half.PositiveInfinity or System.Half.NegativeInfinity; otherwise, false. 587 | public static bool IsInfinity(Half half) { 588 | return HalfHelper.IsInfinity(half); 589 | } 590 | /// 591 | /// Returns a value indicating whether the specified number evaluates to negative infinity. 592 | /// 593 | /// A half-precision floating-point number. 594 | /// true if half evaluates to System.Half.NegativeInfinity; otherwise, false. 595 | public static bool IsNegativeInfinity(Half half) { 596 | return HalfHelper.IsNegativeInfinity(half); 597 | } 598 | /// 599 | /// Returns a value indicating whether the specified number evaluates to positive infinity. 600 | /// 601 | /// A half-precision floating-point number. 602 | /// true if half evaluates to System.Half.PositiveInfinity; otherwise, false. 603 | public static bool IsPositiveInfinity(Half half) { 604 | return HalfHelper.IsPositiveInfinity(half); 605 | } 606 | 607 | #region String operations (Parse and ToString) 608 | /// 609 | /// Converts the string representation of a number to its System.Half equivalent. 610 | /// 611 | /// The string representation of the number to convert. 612 | /// The System.Half number equivalent to the number contained in value. 613 | /// value is null. 614 | /// value is not in the correct format. 615 | /// value represents a number less than System.Half.MinValue or greater than System.Half.MaxValue. 616 | public static Half Parse(string value) { 617 | return (Half)float.Parse(value, CultureInfo.InvariantCulture); 618 | } 619 | /// 620 | /// Converts the string representation of a number to its System.Half equivalent 621 | /// using the specified culture-specific format information. 622 | /// 623 | /// The string representation of the number to convert. 624 | /// An System.IFormatProvider that supplies culture-specific parsing information about value. 625 | /// The System.Half number equivalent to the number contained in s as specified by provider. 626 | /// value is null. 627 | /// value is not in the correct format. 628 | /// value represents a number less than System.Half.MinValue or greater than System.Half.MaxValue. 629 | public static Half Parse(string value, IFormatProvider provider) { 630 | return (Half)float.Parse(value, provider); 631 | } 632 | /// 633 | /// Converts the string representation of a number in a specified style to its System.Half equivalent. 634 | /// 635 | /// The string representation of the number to convert. 636 | /// 637 | /// A bitwise combination of System.Globalization.NumberStyles values that indicates 638 | /// the style elements that can be present in value. A typical value to specify is 639 | /// System.Globalization.NumberStyles.Number. 640 | /// 641 | /// The System.Half number equivalent to the number contained in s as specified by style. 642 | /// value is null. 643 | /// 644 | /// style is not a System.Globalization.NumberStyles value. -or- style is the 645 | /// System.Globalization.NumberStyles.AllowHexSpecifier value. 646 | /// 647 | /// value is not in the correct format. 648 | /// value represents a number less than System.Half.MinValue or greater than System.Half.MaxValue. 649 | public static Half Parse(string value, NumberStyles style) { 650 | return (Half)float.Parse(value, style, CultureInfo.InvariantCulture); 651 | } 652 | /// 653 | /// Converts the string representation of a number to its System.Half equivalent 654 | /// using the specified style and culture-specific format. 655 | /// 656 | /// The string representation of the number to convert. 657 | /// 658 | /// A bitwise combination of System.Globalization.NumberStyles values that indicates 659 | /// the style elements that can be present in value. A typical value to specify is 660 | /// System.Globalization.NumberStyles.Number. 661 | /// 662 | /// An System.IFormatProvider object that supplies culture-specific information about the format of value. 663 | /// The System.Half number equivalent to the number contained in s as specified by style and provider. 664 | /// value is null. 665 | /// 666 | /// style is not a System.Globalization.NumberStyles value. -or- style is the 667 | /// System.Globalization.NumberStyles.AllowHexSpecifier value. 668 | /// 669 | /// value is not in the correct format. 670 | /// value represents a number less than System.Half.MinValue or greater than System.Half.MaxValue. 671 | public static Half Parse(string value, NumberStyles style, IFormatProvider provider) { 672 | return (Half)float.Parse(value, style, provider); 673 | } 674 | /// 675 | /// Converts the string representation of a number to its System.Half equivalent. 676 | /// A return value indicates whether the conversion succeeded or failed. 677 | /// 678 | /// The string representation of the number to convert. 679 | /// 680 | /// When this method returns, contains the System.Half number that is equivalent 681 | /// to the numeric value contained in value, if the conversion succeeded, or is zero 682 | /// if the conversion failed. The conversion fails if the s parameter is null, 683 | /// is not a number in a valid format, or represents a number less than System.Half.MinValue 684 | /// or greater than System.Half.MaxValue. This parameter is passed uninitialized. 685 | /// 686 | /// true if s was converted successfully; otherwise, false. 687 | public static bool TryParse(string value, out Half result) { 688 | float f; 689 | if (float.TryParse(value, out f)) { 690 | result = (Half)f; 691 | return true; 692 | } 693 | 694 | result = new Half(); 695 | return false; 696 | } 697 | /// 698 | /// Converts the string representation of a number to its System.Half equivalent 699 | /// using the specified style and culture-specific format. A return value indicates 700 | /// whether the conversion succeeded or failed. 701 | /// 702 | /// The string representation of the number to convert. 703 | /// 704 | /// A bitwise combination of System.Globalization.NumberStyles values that indicates 705 | /// the permitted format of value. A typical value to specify is System.Globalization.NumberStyles.Number. 706 | /// 707 | /// An System.IFormatProvider object that supplies culture-specific parsing information about value. 708 | /// 709 | /// When this method returns, contains the System.Half number that is equivalent 710 | /// to the numeric value contained in value, if the conversion succeeded, or is zero 711 | /// if the conversion failed. The conversion fails if the s parameter is null, 712 | /// is not in a format compliant with style, or represents a number less than 713 | /// System.Half.MinValue or greater than System.Half.MaxValue. This parameter is passed uninitialized. 714 | /// 715 | /// true if s was converted successfully; otherwise, false. 716 | /// 717 | /// style is not a System.Globalization.NumberStyles value. -or- style 718 | /// is the System.Globalization.NumberStyles.AllowHexSpecifier value. 719 | /// 720 | public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out Half result) { 721 | bool parseResult = false; 722 | float f; 723 | if (float.TryParse(value, style, provider, out f)) { 724 | result = (Half)f; 725 | parseResult = true; 726 | } 727 | else { 728 | result = new Half(); 729 | } 730 | 731 | return parseResult; 732 | } 733 | /// 734 | /// Converts the numeric value of this instance to its equivalent string representation. 735 | /// 736 | /// A string that represents the value of this instance. 737 | public override string ToString() { 738 | return ((float)this).ToString(CultureInfo.InvariantCulture); 739 | } 740 | /// 741 | /// Converts the numeric value of this instance to its equivalent string representation 742 | /// using the specified culture-specific format information. 743 | /// 744 | /// An System.IFormatProvider that supplies culture-specific formatting information. 745 | /// The string representation of the value of this instance as specified by provider. 746 | public string ToString(IFormatProvider formatProvider) { 747 | return ((float)this).ToString(formatProvider); 748 | } 749 | /// 750 | /// Converts the numeric value of this instance to its equivalent string representation, using the specified format. 751 | /// 752 | /// A numeric format string. 753 | /// The string representation of the value of this instance as specified by format. 754 | public string ToString(string format) { 755 | return ((float)this).ToString(format, CultureInfo.InvariantCulture); 756 | } 757 | /// 758 | /// Converts the numeric value of this instance to its equivalent string representation 759 | /// using the specified format and culture-specific format information. 760 | /// 761 | /// A numeric format string. 762 | /// An System.IFormatProvider that supplies culture-specific formatting information. 763 | /// The string representation of the value of this instance as specified by format and provider. 764 | /// format is invalid. 765 | public string ToString(string format, IFormatProvider formatProvider) { 766 | return ((float)this).ToString(format, formatProvider); 767 | } 768 | #endregion 769 | 770 | #region IConvertible Members 771 | float IConvertible.ToSingle(IFormatProvider provider) { 772 | return (float)this; 773 | } 774 | TypeCode IConvertible.GetTypeCode() { 775 | return GetTypeCode(); 776 | } 777 | bool IConvertible.ToBoolean(IFormatProvider provider) { 778 | return Convert.ToBoolean((float)this); 779 | } 780 | byte IConvertible.ToByte(IFormatProvider provider) { 781 | return Convert.ToByte((float)this); 782 | } 783 | char IConvertible.ToChar(IFormatProvider provider) { 784 | throw new InvalidCastException(string.Format(CultureInfo.CurrentCulture, "Invalid cast from '{0}' to '{1}'.", "Half", "Char")); 785 | } 786 | DateTime IConvertible.ToDateTime(IFormatProvider provider) { 787 | throw new InvalidCastException(string.Format(CultureInfo.CurrentCulture, "Invalid cast from '{0}' to '{1}'.", "Half", "DateTime")); 788 | } 789 | decimal IConvertible.ToDecimal(IFormatProvider provider) { 790 | return Convert.ToDecimal((float)this); 791 | } 792 | double IConvertible.ToDouble(IFormatProvider provider) { 793 | return Convert.ToDouble((float)this); 794 | } 795 | short IConvertible.ToInt16(IFormatProvider provider) { 796 | return Convert.ToInt16((float)this); 797 | } 798 | int IConvertible.ToInt32(IFormatProvider provider) { 799 | return Convert.ToInt32((float)this); 800 | } 801 | long IConvertible.ToInt64(IFormatProvider provider) { 802 | return Convert.ToInt64((float)this); 803 | } 804 | sbyte IConvertible.ToSByte(IFormatProvider provider) { 805 | return Convert.ToSByte((float)this); 806 | } 807 | string IConvertible.ToString(IFormatProvider provider) { 808 | return Convert.ToString((float)this, CultureInfo.InvariantCulture); 809 | } 810 | object IConvertible.ToType(Type conversionType, IFormatProvider provider) { 811 | return (((float)this) as IConvertible).ToType(conversionType, provider); 812 | } 813 | ushort IConvertible.ToUInt16(IFormatProvider provider) { 814 | return Convert.ToUInt16((float)this); 815 | } 816 | uint IConvertible.ToUInt32(IFormatProvider provider) { 817 | return Convert.ToUInt32((float)this); 818 | } 819 | ulong IConvertible.ToUInt64(IFormatProvider provider) { 820 | return Convert.ToUInt64((float)this); 821 | } 822 | #endregion 823 | } 824 | } 825 | --------------------------------------------------------------------------------