├── 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 |
--------------------------------------------------------------------------------