diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/AImg.cs b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/AImg.cs index 64169fa..5ad99f0 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/AImg.cs +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/AImg.cs @@ -1,173 +1,212 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using Artomatix.ImageLoader.ImgEncodingOptions; - -namespace Artomatix.ImageLoader -{ - public class AImg : IDisposable - { - private IntPtr nativeHandle = IntPtr.Zero; - private Stream stream; - private bool doDisposeStream; - private bool disposed = false; - - private ImgLoader.ReadCallback readCallback; - private ImgLoader.TellCallback tellCallback; - private ImgLoader.SeekCallback seekCallback; - - private Int32 _width, _height; - private Int32 _numChannels; - private Int32 _bytesPerChannel; - - public int width { get { return _width; } } - public int height { get { return _height; } } - public int numChannels { get { return _numChannels; } } - public int bytesPerChannel { get { return _bytesPerChannel; } } - public AImgFloatOrIntType floatOrInt { get; private set; } - public AImgFileFormat detectedFileFormat { get; private set; } - public AImgFormat decodedImgFormat { get; private set; } - public string colourProfileName { get; private set; } - public byte[] colourProfile { get; private set; } - - public AImg(AImgFileFormat fmt) - { - nativeHandle = NativeFuncs.inst.AImgGetAImg((Int32)fmt); - } - - /// - /// Opens an AImg from a stream. - /// - /// If set to true stream will be disposed whne the AImg is disposed. - public unsafe AImg(Stream stream, bool doDisposeStream = true) - { - if (!stream.CanRead || !stream.CanSeek) - throw new Exception("unusable stream"); - - this.stream = stream; - this.doDisposeStream = doDisposeStream; - - readCallback = ImgLoader.getReadCallback(stream); - tellCallback = ImgLoader.getTellCallback(stream); - seekCallback = ImgLoader.getSeekCallback(stream); - - Int32 detectedImageFormatTmp = 0; - Int32 errCode = NativeFuncs.inst.AImgOpen(readCallback, tellCallback, seekCallback, IntPtr.Zero, out nativeHandle, out detectedImageFormatTmp); - AImgException.checkErrorCode(nativeHandle, errCode); - - detectedFileFormat = (AImgFileFormat)detectedImageFormatTmp; - - Int32 floatOrIntTmp = 0; - Int32 decodedImgFormatTmp = 0; - Int32 colourProfileLen = 0; - NativeFuncs.inst.AImgGetInfo(nativeHandle, out _width, out _height, out _numChannels, out _bytesPerChannel, out floatOrIntTmp, out decodedImgFormatTmp, out colourProfileLen); - floatOrInt = (AImgFloatOrIntType)floatOrIntTmp; - decodedImgFormat = (AImgFormat)decodedImgFormatTmp; - colourProfile = new byte[colourProfileLen]; - fixed (byte* array = colourProfile) - { - System.Text.StringBuilder _colourProfileName = new System.Text.StringBuilder(30); - NativeFuncs.inst.AImgGetColourProfile(nativeHandle, _colourProfileName, (IntPtr)array, out colourProfileLen); - colourProfileName = _colourProfileName.ToString(); - } - } - - /// - /// Decodes the image into a user-specified buffer. - /// - /// This buffer can be of any struct type, so for example for an RGBA8 image, you might use a byte array, - /// or a custom struct with byte fields for the r, g, b, and a components. A custom struct would need the [StructLayout(LayoutKind.Sequential), Serializable] attribute. - public void decodeImage(T[] destBuffer, AImgFormat forceImageFormat = AImgFormat.INVALID_FORMAT) where T : struct - { - uint size = (uint)(Marshal.SizeOf(default(T))); - - if (size * destBuffer.Length < decodedImgFormat.sizeInBytes() * width * height) - throw new ArgumentException("destBuffer is too small for this image"); - - GCHandle pinnedArray = GCHandle.Alloc(destBuffer, GCHandleType.Pinned); - IntPtr pointer = pinnedArray.AddrOfPinnedObject(); - - Int32 errCode = NativeFuncs.inst.AImgDecodeImage(nativeHandle, pointer, (Int32)forceImageFormat); - AImgException.checkErrorCode(nativeHandle, errCode); - - pinnedArray.Free(); - } - - public static AImgFormat getWhatFormatWillBeWrittenForData(AImgFileFormat fileFormat, AImgFormat format) - { - return (AImgFormat)NativeFuncs.inst.AImgGetWhatFormatWillBeWrittenForData((Int32)fileFormat, (Int32)format); - } - - public unsafe void writeImage(T[] data, int width, int height, AImgFormat format, string profileName, byte[] colourProfile, Stream s, FormatEncodeOptions options = null) where T : struct - { - var writeCallback = ImgLoader.getWriteCallback(s); - var tellCallback = ImgLoader.getTellCallback(s); - var seekCallback = ImgLoader.getSeekCallback(s); - - if (profileName == string.Empty) - profileName = "empty"; - - GCHandle pinnedArray = GCHandle.Alloc(data, GCHandleType.Pinned); - IntPtr pointer = pinnedArray.AddrOfPinnedObject(); - - fixed (byte* colourProfileArray = colourProfile) - { - GCHandle encodeOptionsHandle = default(GCHandle); - IntPtr encodeOptionsPtr = IntPtr.Zero; - - if (options != null) - { - encodeOptionsHandle = GCHandle.Alloc(options, GCHandleType.Pinned); - encodeOptionsPtr = encodeOptionsHandle.AddrOfPinnedObject(); - } - try - { - int colourProfileLength = 0; - if (colourProfile != null) - { - colourProfileLength = colourProfile.Length; - } - - Int32 errCode = NativeFuncs.inst.AImgWriteImage(nativeHandle, pointer, width, height, (Int32)format, profileName, (IntPtr)colourProfileArray, colourProfileLength, writeCallback, tellCallback, seekCallback, IntPtr.Zero, encodeOptionsPtr); - AImgException.checkErrorCode(nativeHandle, errCode); - } - finally - { - pinnedArray.Free(); - - if (encodeOptionsPtr != IntPtr.Zero) - encodeOptionsHandle.Free(); - } - } - GC.KeepAlive(writeCallback); - GC.KeepAlive(tellCallback); - GC.KeepAlive(seekCallback); - } - - private void Dispose(bool disposing) - { - if (!disposed) - { - disposed = true; - - if (disposing) - GC.SuppressFinalize(this); - - NativeFuncs.inst.AImgClose(nativeHandle); - - if (doDisposeStream) - stream.Dispose(); - } - } - - public void Dispose() - { - Dispose(true); - } - - ~AImg() - { - Dispose(false); - } - } +using System; +using System.IO; +using System.Runtime.InteropServices; +using Artomatix.ImageLoader.ImgEncodingOptions; + +namespace Artomatix.ImageLoader +{ + public class AImg : IDisposable + { + private IntPtr nativeHandle = IntPtr.Zero; + private Stream stream; + private bool doDisposeStream; + private bool disposed = false; + + private ImgLoader.ReadCallback readCallback; + private ImgLoader.TellCallback tellCallback; + private ImgLoader.SeekCallback seekCallback; + + private Int32 _width, _height; + private Int32 _numChannels; + private Int32 _bytesPerChannel; + + public int width { get { return _width; } } + public int height { get { return _height; } } + public int numChannels { get { return _numChannels; } } + public int bytesPerChannel { get { return _bytesPerChannel; } } + public AImgFloatOrIntType floatOrInt { get; private set; } + public AImgFileFormat detectedFileFormat { get; private set; } + public AImgFormat decodedImgFormat { get; private set; } + public string colourProfileName { get; private set; } + public byte[] colourProfile { get; private set; } + + public AImg(AImgFileFormat fmt) + { + nativeHandle = NativeFuncs.inst.AImgGetAImg((Int32)fmt); + } + + /// + /// Opens an AImg from a stream. + /// + /// If set to true stream will be disposed whne the AImg is disposed. + public unsafe AImg(Stream stream, bool doDisposeStream = true) + { + if (!stream.CanRead || !stream.CanSeek) + throw new Exception("unusable stream"); + + this.stream = stream; + this.doDisposeStream = doDisposeStream; + + readCallback = ImgLoader.getReadCallback(stream); + tellCallback = ImgLoader.getTellCallback(stream); + seekCallback = ImgLoader.getSeekCallback(stream); + + Int32 detectedImageFormatTmp = 0; + Int32 errCode = NativeFuncs.inst.AImgOpen(readCallback, tellCallback, seekCallback, IntPtr.Zero, out nativeHandle, out detectedImageFormatTmp); + AImgException.checkErrorCode(nativeHandle, errCode); + + detectedFileFormat = (AImgFileFormat)detectedImageFormatTmp; + + Int32 floatOrIntTmp = 0; + Int32 decodedImgFormatTmp = 0; + Int32 colourProfileLen = 0; + NativeFuncs.inst.AImgGetInfo(nativeHandle, out _width, out _height, out _numChannels, out _bytesPerChannel, out floatOrIntTmp, out decodedImgFormatTmp, out colourProfileLen); + floatOrInt = (AImgFloatOrIntType)floatOrIntTmp; + decodedImgFormat = (AImgFormat)decodedImgFormatTmp; + colourProfile = new byte[colourProfileLen]; + fixed (byte* array = colourProfile) + { + System.Text.StringBuilder _colourProfileName = new System.Text.StringBuilder(30); + NativeFuncs.inst.AImgGetColourProfile(nativeHandle, _colourProfileName, (IntPtr)array, out colourProfileLen); + colourProfileName = _colourProfileName.ToString(); + } + } + + /// + /// Decodes the image into a user-specified buffer. + /// + /// This buffer can be of any struct type, so for example for an RGBA8 image, you might use a byte array, + /// or a custom struct with byte fields for the r, g, b, and a components. A custom struct would need the [StructLayout(LayoutKind.Sequential), Serializable] attribute. + public void decodeImage(T[] destBuffer, AImgFormat forceImageFormat = AImgFormat.INVALID_FORMAT) where T : struct + { + uint size = (uint)(Marshal.SizeOf(default(T))); + + if (size * destBuffer.Length < decodedImgFormat.sizeInBytes() * width * height) + throw new ArgumentException("destBuffer is too small for this image"); + + GCHandle pinnedArray = GCHandle.Alloc(destBuffer, GCHandleType.Pinned); + IntPtr pointer = pinnedArray.AddrOfPinnedObject(); + + Int32 errCode = NativeFuncs.inst.AImgDecodeImage(nativeHandle, pointer, (Int32)forceImageFormat); + AImgException.checkErrorCode(nativeHandle, errCode); + + pinnedArray.Free(); + } + + public static bool IsFormatSupported(AImgFileFormat fileFormat, AImgFormat outputFormat) + { + return NativeFuncs.inst.AImgIsFormatSupported((Int32)fileFormat, (Int32)outputFormat); + } + + public static AImgFormat getWhatFormatWillBeWrittenForData(AImgFileFormat fileFormat, AImgFormat inputFormat, AImgFormat outputFormat) + { + return (AImgFormat)NativeFuncs.inst.AImgGetWhatFormatWillBeWrittenForData((Int32)fileFormat, (Int32)inputFormat, (Int32)outputFormat); + } + + public static AImgFormat ChangeFormatBitDepth(AImgFormat format, AImgFormat newBitDepth) + { + return (AImgFormat)NativeFuncs.inst.AIChangeBitDepth((Int32)format, (Int32)newBitDepth); + } + + public static AImgFormat GetBitDepth(AImgFormat format) + { + return (AImgFormat)NativeFuncs.inst.AIGetBitDepth((Int32)format); + } + + public unsafe void writeImage(T[] data, + int width, + int height, + AImgFormat inputFormat, + string profileName, byte[] colourProfile, + Stream s, + FormatEncodeOptions options = null) where T : struct + { + writeImage(data, width, height, + inputFormat, inputFormat, + profileName, colourProfile, + s, options); + } + + public unsafe void writeImage(T[] data, + int width, + int height, + AImgFormat inputFormat, + AImgFormat outputFormat, + string profileName, byte[] colourProfile, + Stream s, + FormatEncodeOptions options = null) where T : struct + { + var writeCallback = ImgLoader.getWriteCallback(s); + var tellCallback = ImgLoader.getTellCallback(s); + var seekCallback = ImgLoader.getSeekCallback(s); + + if (profileName == string.Empty) + profileName = "empty"; + + GCHandle pinnedArray = GCHandle.Alloc(data, GCHandleType.Pinned); + IntPtr pointer = pinnedArray.AddrOfPinnedObject(); + + fixed (byte* colourProfileArray = colourProfile) + { + GCHandle encodeOptionsHandle = default(GCHandle); + IntPtr encodeOptionsPtr = IntPtr.Zero; + + if (options != null) + { + encodeOptionsHandle = GCHandle.Alloc(options, GCHandleType.Pinned); + encodeOptionsPtr = encodeOptionsHandle.AddrOfPinnedObject(); + } + try + { + int colourProfileLength = 0; + if (colourProfile != null) + { + colourProfileLength = colourProfile.Length; + } + + Int32 errCode = NativeFuncs.inst.AImgWriteImage(nativeHandle, pointer, width, height, + (Int32)inputFormat, (Int32)outputFormat, + profileName, (IntPtr)colourProfileArray, colourProfileLength, + writeCallback, tellCallback, seekCallback, IntPtr.Zero, encodeOptionsPtr); + AImgException.checkErrorCode(nativeHandle, errCode); + } + finally + { + pinnedArray.Free(); + + if (encodeOptionsPtr != IntPtr.Zero) + encodeOptionsHandle.Free(); + } + } + GC.KeepAlive(writeCallback); + GC.KeepAlive(tellCallback); + GC.KeepAlive(seekCallback); + } + + private void Dispose(bool disposing) + { + if (!disposed) + { + disposed = true; + + if (disposing) + GC.SuppressFinalize(this); + + NativeFuncs.inst.AImgClose(nativeHandle); + + if (doDisposeStream) + stream.Dispose(); + } + } + + public void Dispose() + { + Dispose(true); + } + + ~AImg() + { + Dispose(false); + } + } } \ No newline at end of file diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Artomatix.ImageLoader.csproj b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Artomatix.ImageLoader.csproj index 261970d..385f454 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Artomatix.ImageLoader.csproj +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Artomatix.ImageLoader.csproj @@ -1,130 +1,104 @@ - - - - Debug - x64 - {C0BB2915-8150-4817-9351-2B7F83D96F2A} - Library - Artomatix.ImageLoader - Artomatix.ImageLoader - v4.6 - 12.0.0 - 2.0 - - - - - - true - bin\x64\Debug\ - DEBUG; - true - full - x64 - prompt - MinimumRecommendedRules.ruleset - 4 - false - - - bin\x64\Release\ - true - true - full - x64 - prompt - MinimumRecommendedRules.ruleset - 4 - - - true - bin\x86\Debug\ - DEBUG; - true - full - x86 - prompt - MinimumRecommendedRules.ruleset - 4 - false - - - bin\x86\Release\ - true - true - full - x86 - prompt - MinimumRecommendedRules.ruleset - 4 - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - - ..\..\..\..\packages\NativeBinaryManager\lib\NativeBinaryManager.dll - True - True - - - - - - - - - ..\..\..\..\packages\NUnit\lib\nunit.framework.dll - True - True - - - - - - - - - ..\..\..\..\packages\Stugo.Interop\lib\Stugo.Interop.dll - True - True - - - - - - - - - ..\..\..\..\packages\ZipStorer\lib\net20\ZipStorer.dll - True - True - - - - + + + + + Debug + x64 + {C0BB2915-8150-4817-9351-2B7F83D96F2A} + Library + Artomatix.ImageLoader + Artomatix.ImageLoader + v4.6 + 512 + + + + true + bin\x64\Debug\ + DEBUG; + true + full + x64 + prompt + MinimumRecommendedRules.ruleset + 4 + false + + + bin\x64\Release\ + true + true + full + x64 + prompt + MinimumRecommendedRules.ruleset + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\..\packages\NativeBinaryManager\lib\NativeBinaryManager.dll + True + True + + + + + + + + + ..\..\..\..\packages\NUnit\lib\nunit.framework.dll + True + True + + + + + + + + + ..\..\..\..\packages\Stugo.Interop\lib\Stugo.Interop.dll + True + True + + + + + + + + + ..\..\..\..\packages\ZipStorer\lib\net452\ZipStorer.dll + True + True + + + + \ No newline at end of file diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Enums.cs b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Enums.cs index 4e2218c..04edba7 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Enums.cs +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Enums.cs @@ -17,34 +17,48 @@ public enum AImgFileFormat PNG_IMAGE_FORMAT = 2, JPEG_IMAGE_FORMAT = 3, TGA_IMAGE_FORMAT = 4, - TIFF_IMAGE_FORMAT = 5 + TIFF_IMAGE_FORMAT = 5, + HDR_IMAGE_FORMAT = 6 }; // format is [channels][bits per channel][U/F] // U means unsigned normalised, so eg 8U maps integer vals 0-255 to float range 0-1, F means an normal float value + + [Flags] public enum AImgFormat { INVALID_FORMAT = -1, - R8U = 0, - RG8U = 1, - RGB8U = 2, - RGBA8U = 3, - - R16U = 4, - RG16U = 5, - RGB16U = 6, - RGBA16U = 7, - - R16F = 8, - RG16F = 9, - RGB16F = 10, - RGBA16F = 11, - - R32F = 12, - RG32F = 13, - RGB32F = 14, - RGBA32F = 15 + _8BITS = 1 << 0, + _16BITS = 1 << 5, + _32BITS = 1 << 6, + + R = 1 << 1, + RG = 1 << 2, + RGB = 1 << 3, + RGBA = 1 << 4, + + FLOAT_FORMAT = 1 << 7, + + R8U = R | _8BITS, + RG8U = RG | _8BITS, + RGB8U = RGB | _8BITS, + RGBA8U = RGBA | _8BITS, + + R16U = R | _16BITS, + RG16U = RG | _16BITS, + RGB16U = RGB | _16BITS, + RGBA16U = RGBA | _16BITS, + + R16F = R | _16BITS | FLOAT_FORMAT, + RG16F = RG | _16BITS | FLOAT_FORMAT, + RGB16F = RGB | _16BITS | FLOAT_FORMAT, + RGBA16F = RGBA | _16BITS | FLOAT_FORMAT, + + R32F = R | _32BITS | FLOAT_FORMAT, + RG32F = RG | _32BITS | FLOAT_FORMAT, + RGB32F = RGB | _32BITS | FLOAT_FORMAT, + RGBA32F = RGBA | _32BITS | FLOAT_FORMAT }; public static class AImgFormatExtension diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Exceptions.cs b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Exceptions.cs index 69de3f1..509d628 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Exceptions.cs +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Exceptions.cs @@ -1,5 +1,6 @@ using System; - +using System.Runtime.Serialization; + namespace Artomatix.ImageLoader { public class AImgException : Exception @@ -37,14 +38,23 @@ public static void checkErrorCode(IntPtr img, Int32 errorCode) throw new AImgOpenFailedEmptyInputException(msg); case -9: throw new AImgInvalidEncodeArgsException(msg); + case -10: + throw new AImgWriteNotSupportedForFormat(msg); default: throw new AImgException("Unknown error code: " + errorCode + " " + msg); } } } - } - + } + + internal class AImgWriteNotSupportedForFormat : Exception + { + public AImgWriteNotSupportedForFormat(string message) : base(message) + { + } + } + public class AImgUnsupportedFiletypeException : AImgException { public AImgUnsupportedFiletypeException(string msg) : base(msg) diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/NativeFuncs.cs b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/NativeFuncs.cs index e386377..3b4e96f 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/NativeFuncs.cs +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/NativeFuncs.cs @@ -13,7 +13,7 @@ internal class NativeFuncs { private static NativeFuncs initNative() { - var dllPath = Path.GetFullPath("AIMG.dll"); + var dllPath = Path.GetFullPath($"{AppDomain.CurrentDomain.BaseDirectory}/AIMG.dll"); #if DEBUG var zipStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Artomatix.ImageLoader.embedded_files.binaries.zip"); NativeBinaryManager.NativeBinaryManager.ExtractNativeBinary(zipStream, dllPath); @@ -69,7 +69,22 @@ private static NativeFuncs initNative() [EntryPoint("AImgCleanUp")] public AImgCleanUp_t AImgCleanUp; - public delegate Int32 AImgGetWhatFormatWillBeWrittenForData_t(Int32 fileFormat, Int32 inputFormat); + public delegate Int32 AIChangeBitDepth_t(Int32 format, Int32 newBitDepth); + + [EntryPoint("AIChangeBitDepth")] + public AIChangeBitDepth_t AIChangeBitDepth; + + public delegate Int32 AIGetBitDepth_t(Int32 format); + + [EntryPoint("AIGetBitDepth")] + public AIGetBitDepth_t AIGetBitDepth; + + public delegate bool AImgIsFormatSupported_t(Int32 fileFormat, Int32 outputFormat); + + [EntryPoint("AImgIsFormatSupported")] + public AImgIsFormatSupported_t AImgIsFormatSupported; + + public delegate Int32 AImgGetWhatFormatWillBeWrittenForData_t(Int32 fileFormat, Int32 inputFormat, Int32 outputFormat); [EntryPoint("AImgGetWhatFormatWillBeWrittenForData")] public AImgGetWhatFormatWillBeWrittenForData_t AImgGetWhatFormatWillBeWrittenForData; @@ -87,7 +102,8 @@ out Int32 detectedFileFormat public AImgOpen_t AImgOpen; public delegate Int32 AImgWriteImage_t( - IntPtr img, IntPtr data, Int32 width, Int32 height, Int32 inputFormat, string profileName, IntPtr colourProfile, Int32 colourProfileLength, + IntPtr img, IntPtr data, Int32 width, Int32 height, Int32 inputFormat, Int32 outputFormat, + string profileName, IntPtr colourProfile, Int32 colourProfileLength, [MarshalAs(UnmanagedType.FunctionPtr)] ImgLoader.WriteCallback writeCallback, [MarshalAs(UnmanagedType.FunctionPtr)] ImgLoader.TellCallback tellCallback, [MarshalAs(UnmanagedType.FunctionPtr)] ImgLoader.SeekCallback seekCallback, diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Properties/AssemblyInfo.cs b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Properties/AssemblyInfo.cs index 7d10efb..8bed5fd 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Properties/AssemblyInfo.cs +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoader/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("wheybags")] +[assembly: AssemblyCopyright("Artomatix Limited")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/ArtomatixImageLoaderTests.csproj b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/ArtomatixImageLoaderTests.csproj index 55f23b3..6d95b1e 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/ArtomatixImageLoaderTests.csproj +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/ArtomatixImageLoaderTests.csproj @@ -107,7 +107,7 @@ - ..\..\..\..\packages\ZipStorer\lib\net20\ZipStorer.dll + ..\..\..\..\packages\ZipStorer\lib\net452\ZipStorer.dll True True diff --git a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/TestAImg.cs b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/TestAImg.cs index bd66131..0de42f4 100644 --- a/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/TestAImg.cs +++ b/bindings/csharp/ArtomatixImageLoader/ArtomatixImageLoaderTests/TestAImg.cs @@ -98,10 +98,34 @@ public static void TestForceImageFormat() [Test] public static void TestGetWhatFormatWIllBeWritten() { - AImgFormat res = AImg.getWhatFormatWillBeWrittenForData(AImgFileFormat.EXR_IMAGE_FORMAT, AImgFormat.RGBA32F); + AImgFormat res = AImg.getWhatFormatWillBeWrittenForData(AImgFileFormat.EXR_IMAGE_FORMAT, AImgFormat.RGBA32F, AImgFormat.INVALID_FORMAT); Assert.AreEqual(AImgFormat.RGBA32F, res); } + [Test] + public static void TestSupportedFormat() + { + Assert.True(AImg.IsFormatSupported(AImgFileFormat.TIFF_IMAGE_FORMAT, AImgFormat._8BITS)); + Assert.True(AImg.IsFormatSupported(AImgFileFormat.TIFF_IMAGE_FORMAT, AImgFormat._16BITS)); + Assert.True(AImg.IsFormatSupported(AImgFileFormat.TIFF_IMAGE_FORMAT, AImgFormat._32BITS)); + + Assert.False(AImg.IsFormatSupported(AImgFileFormat.EXR_IMAGE_FORMAT, AImgFormat._8BITS)); + Assert.True(AImg.IsFormatSupported(AImgFileFormat.EXR_IMAGE_FORMAT, AImgFormat._16BITS)); + Assert.True(AImg.IsFormatSupported(AImgFileFormat.EXR_IMAGE_FORMAT, AImgFormat._32BITS)); + + Assert.True(AImg.IsFormatSupported(AImgFileFormat.JPEG_IMAGE_FORMAT, AImgFormat._8BITS)); + Assert.False(AImg.IsFormatSupported(AImgFileFormat.JPEG_IMAGE_FORMAT, AImgFormat._16BITS)); + Assert.False(AImg.IsFormatSupported(AImgFileFormat.JPEG_IMAGE_FORMAT, AImgFormat._32BITS)); + + Assert.True(AImg.IsFormatSupported(AImgFileFormat.PNG_IMAGE_FORMAT, AImgFormat._8BITS)); + Assert.True(AImg.IsFormatSupported(AImgFileFormat.PNG_IMAGE_FORMAT, AImgFormat._16BITS)); + Assert.False(AImg.IsFormatSupported(AImgFileFormat.PNG_IMAGE_FORMAT, AImgFormat._32BITS)); + + Assert.True(AImg.IsFormatSupported(AImgFileFormat.TGA_IMAGE_FORMAT, AImgFormat._8BITS)); + Assert.False(AImg.IsFormatSupported(AImgFileFormat.TGA_IMAGE_FORMAT, AImgFormat._16BITS)); + Assert.False(AImg.IsFormatSupported(AImgFileFormat.TGA_IMAGE_FORMAT, AImgFormat._32BITS)); + } + [Test] public static void TestWriteExr() { diff --git a/paket.dependencies b/paket.dependencies index 739e5c7..1cdb6f9 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -1,6 +1,7 @@ framework: net46 source https://api.nuget.org/v3/index.json -nuget NativeCodeBuilder 0.1.3 framework: >= net45, content: once +source ./built_packages +nuget NativeCodeBuilder 0.3.1 framework: >= net45, content: once nuget NUnit == 2.6.4 nuget Stugo.Interop == 0.2 diff --git a/paket.lock b/paket.lock index e37debf..7f71abc 100644 --- a/paket.lock +++ b/paket.lock @@ -3,8 +3,8 @@ NUGET remote: https://api.nuget.org/v3/index.json NativeBinaryManager (0.1) - content: once ZipStorer (>= 2.38) - NativeCodeBuilder (0.1.3) - content: once + NativeCodeBuilder (0.3.1) - content: once, framework: >= net45 NativeBinaryManager (0.1) NUnit (2.6.4) Stugo.Interop (0.2) - ZipStorer (2.38) - content: once + ZipStorer (3.4) - content: once diff --git a/src_c/AIL.cpp b/src_c/AIL.cpp index c5b4d1e..548c9cb 100644 --- a/src_c/AIL.cpp +++ b/src_c/AIL.cpp @@ -12,40 +12,44 @@ #include "jpeg.h" #include "tga.h" #include "tiff.h" +#include "hdr.h" #ifdef HAVE_EXR - #include +#include #endif std::map loaders; - int32_t AImgInitialise() { - #ifdef HAVE_EXR - loaders[AImgFileFormat::EXR_IMAGE_FORMAT] = new AImg::ExrImageLoader(); - #endif +#ifdef HAVE_EXR + loaders[AImgFileFormat::EXR_IMAGE_FORMAT] = new AImg::ExrImageLoader(); +#endif + +#ifdef HAVE_PNG + loaders[AImgFileFormat::PNG_IMAGE_FORMAT] = new AImg::PNGImageLoader(); +#endif - #ifdef HAVE_PNG - loaders[AImgFileFormat::PNG_IMAGE_FORMAT] = new AImg::PNGImageLoader(); - #endif +#ifdef HAVE_JPEG + loaders[AImgFileFormat::JPEG_IMAGE_FORMAT] = new AImg::JPEGImageLoader(); +#endif - #ifdef HAVE_JPEG - loaders[AImgFileFormat::JPEG_IMAGE_FORMAT] = new AImg::JPEGImageLoader(); - #endif +#ifdef HAVE_TGA + loaders[AImgFileFormat::TGA_IMAGE_FORMAT] = new AImg::TGAImageLoader(); +#endif - #ifdef HAVE_TGA - loaders[AImgFileFormat::TGA_IMAGE_FORMAT] = new AImg::TGAImageLoader(); - #endif +#ifdef HAVE_TIFF + loaders[AImgFileFormat::TIFF_IMAGE_FORMAT] = new AImg::TIFFImageLoader(); +#endif - #ifdef HAVE_TIFF - loaders[AImgFileFormat::TIFF_IMAGE_FORMAT] = new AImg::TIFFImageLoader(); - #endif +#ifdef HAVE_HDR + loaders[AImgFileFormat::HDR_IMAGE_FORMAT] = new AImg::HDRImageLoader(); +#endif - for(auto it = loaders.begin(); it != loaders.end(); ++it) + for (auto it = loaders.begin(); it != loaders.end(); ++it) { int32_t err = it->second->initialise(); - if(err != AImgErrorCode::AIMG_SUCCESS) + if (err != AImgErrorCode::AIMG_SUCCESS) return err; } @@ -54,7 +58,7 @@ int32_t AImgInitialise() void AImgCleanUp() { - for(auto it = loaders.begin(); it != loaders.end(); ++it) + for (auto it = loaders.begin(); it != loaders.end(); ++it) delete it->second; loaders.clear(); @@ -73,7 +77,7 @@ int32_t AImgOpen(ReadCallback readCallback, TellCallback tellCallback, SeekCallb int32_t startPos = tellCallback(callbackData); uint8_t testByte; - if(readCallback(callbackData, &testByte, 1) != 1) + if (readCallback(callbackData, &testByte, 1) != 1) return AImgErrorCode::AIMG_OPEN_FAILED_EMPTY_INPUT; seekCallback(callbackData, startPos); @@ -81,9 +85,9 @@ int32_t AImgOpen(ReadCallback readCallback, TellCallback tellCallback, SeekCallb int32_t fileFormat = UNKNOWN_IMAGE_FORMAT; int32_t retval = AIMG_UNSUPPORTED_FILETYPE; - for(auto it = loaders.begin(); it != loaders.end(); ++it) + for (auto it = loaders.begin(); it != loaders.end(); ++it) { - if(it->second->canLoadImage(readCallback, tellCallback, seekCallback, callbackData)) + if (it->second->canLoadImage(readCallback, tellCallback, seekCallback, callbackData)) { fileFormat = it->second->getAImgFileFormatValue(); @@ -95,7 +99,7 @@ int32_t AImgOpen(ReadCallback readCallback, TellCallback tellCallback, SeekCallb } } - if(detectedFileFormat != NULL) + if (detectedFileFormat != NULL) *detectedFileFormat = fileFormat; return retval; @@ -136,220 +140,221 @@ AImgHandle AImgGetAImg(int32_t fileFormat) return loaders[fileFormat]->getAImg(); } -int32_t AImgWriteImage(AImgHandle imgH, void* data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, - WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, void* encodingOptions) +int32_t AImgWriteImage(AImgHandle imgH, void* data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, void* encodingOptions) { AImg::AImgBase* img = (AImg::AImgBase*)imgH; int32_t err = img->verifyEncodeOptions(encodingOptions); - if(err != AImgErrorCode::AIMG_SUCCESS) + if (err != AImgErrorCode::AIMG_SUCCESS) return err; - return img->writeImage(data, width, height, inputFormat, profileName, colourProfile, colourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, encodingOptions); + return img->writeImage(data, width, height, inputFormat, outputFormat, profileName, colourProfile, colourProfileLen, + writeCallback, tellCallback, seekCallback, callbackData, encodingOptions); } void convertToRGBA32F(void* src, std::vector& dest, size_t i, int32_t inFormat) { switch (inFormat) { - case AImgFormat::R8U: - { - uint8_t* srcF = ((uint8_t*)src) + (i*1); + case AImgFormat::R8U: + { + uint8_t* srcF = ((uint8_t*)src) + (i * 1); - dest[0] = ((float)srcF[0]) / 255.0f; - dest[1] = ((float)srcF[0]) / 255.0f; - dest[2] = ((float)srcF[0]) / 255.0f; - dest[3] = 1; + dest[0] = ((float)srcF[0]) / 255.0f; + dest[1] = ((float)srcF[0]) / 255.0f; + dest[2] = ((float)srcF[0]) / 255.0f; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RG8U: - { - uint8_t* srcF = ((uint8_t*)src) + (i*2); + case AImgFormat::RG8U: + { + uint8_t* srcF = ((uint8_t*)src) + (i * 2); - dest[0] = ((float)srcF[0]) / 255.0f; - dest[1] = ((float)srcF[1]) / 255.0f; - dest[2] = 0; - dest[3] = 1; + dest[0] = ((float)srcF[0]) / 255.0f; + dest[1] = ((float)srcF[1]) / 255.0f; + dest[2] = 0; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGB8U: - { - uint8_t* srcF = ((uint8_t*)src) + (i*3); + case AImgFormat::RGB8U: + { + uint8_t* srcF = ((uint8_t*)src) + (i * 3); - dest[0] = ((float)srcF[0]) / 255.0f; - dest[1] = ((float)srcF[1]) / 255.0f; - dest[2] = ((float)srcF[2]) / 255.0f; - dest[3] = 1; + dest[0] = ((float)srcF[0]) / 255.0f; + dest[1] = ((float)srcF[1]) / 255.0f; + dest[2] = ((float)srcF[2]) / 255.0f; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGBA8U: - { - uint8_t* srcF = ((uint8_t*)src) + (i*4); + case AImgFormat::RGBA8U: + { + uint8_t* srcF = ((uint8_t*)src) + (i * 4); - dest[0] = ((float)srcF[0]) / 255.0f; - dest[1] = ((float)srcF[1]) / 255.0f; - dest[2] = ((float)srcF[2]) / 255.0f; - dest[3] = ((float)srcF[3]) / 255.0f; + dest[0] = ((float)srcF[0]) / 255.0f; + dest[1] = ((float)srcF[1]) / 255.0f; + dest[2] = ((float)srcF[2]) / 255.0f; + dest[3] = ((float)srcF[3]) / 255.0f; - break; - } + break; + } - #ifdef HAVE_EXR - case AImgFormat::R16F: - { - half* srcF = ((half*)src) + (i*1); +#ifdef HAVE_EXR + case AImgFormat::R16F: + { + half* srcF = ((half*)src) + (i * 1); - dest[0] = (float)srcF[0]; - dest[1] = (float)srcF[0]; - dest[2] = (float)srcF[0]; - dest[3] = 1; + dest[0] = (float)srcF[0]; + dest[1] = (float)srcF[0]; + dest[2] = (float)srcF[0]; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RG16F: - { - half* srcF = ((half*)src) + (i*2); + case AImgFormat::RG16F: + { + half* srcF = ((half*)src) + (i * 2); - dest[0] = (float)srcF[0]; - dest[1] = (float)srcF[1]; - dest[2] = 0; - dest[3] = 1; + dest[0] = (float)srcF[0]; + dest[1] = (float)srcF[1]; + dest[2] = 0; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGB16F: - { - half* srcF = ((half*)src) + (i*3); + case AImgFormat::RGB16F: + { + half* srcF = ((half*)src) + (i * 3); - dest[0] = (float)srcF[0]; - dest[1] = (float)srcF[1]; - dest[2] = (float)srcF[2]; - dest[3] = 1; + dest[0] = (float)srcF[0]; + dest[1] = (float)srcF[1]; + dest[2] = (float)srcF[2]; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGBA16F: - { - half* srcF = ((half*)src) + (i*4); + case AImgFormat::RGBA16F: + { + half* srcF = ((half*)src) + (i * 4); - dest[0] = (float)srcF[0]; - dest[1] = (float)srcF[1]; - dest[2] = (float)srcF[2]; - dest[3] = (float)srcF[3]; + dest[0] = (float)srcF[0]; + dest[1] = (float)srcF[1]; + dest[2] = (float)srcF[2]; + dest[3] = (float)srcF[3]; - break; - } - #endif + break; + } +#endif - case AImgFormat::R16U: - { - uint16_t* srcF = ((uint16_t*)src) + (i*1); + case AImgFormat::R16U: + { + uint16_t* srcF = ((uint16_t*)src) + (i * 1); - dest[0] = ((float)srcF[0]) / 65535.0f; - dest[1] = ((float)srcF[0]) / 65535.0f; - dest[2] = ((float)srcF[0]) / 65535.0f; - dest[3] = 1; + dest[0] = ((float)srcF[0]) / 65535.0f; + dest[1] = ((float)srcF[0]) / 65535.0f; + dest[2] = ((float)srcF[0]) / 65535.0f; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RG16U: - { - uint16_t* srcF = ((uint16_t*)src) + (i*2); + case AImgFormat::RG16U: + { + uint16_t* srcF = ((uint16_t*)src) + (i * 2); - dest[0] = ((float)srcF[0]) / 65535.0f; - dest[1] = ((float)srcF[1]) / 65535.0f; - dest[2] = 0; - dest[3] = 1; + dest[0] = ((float)srcF[0]) / 65535.0f; + dest[1] = ((float)srcF[1]) / 65535.0f; + dest[2] = 0; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGB16U: - { - uint16_t* srcF = ((uint16_t*)src) + (i*3); + case AImgFormat::RGB16U: + { + uint16_t* srcF = ((uint16_t*)src) + (i * 3); - dest[0] = ((float)srcF[0]) / 65535.0f; - dest[1] = ((float)srcF[1]) / 65535.0f; - dest[2] = ((float)srcF[2]) / 65535.0f; - dest[3] = 1; + dest[0] = ((float)srcF[0]) / 65535.0f; + dest[1] = ((float)srcF[1]) / 65535.0f; + dest[2] = ((float)srcF[2]) / 65535.0f; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGBA16U: - { - uint16_t* srcF = ((uint16_t*)src) + (i*4); + case AImgFormat::RGBA16U: + { + uint16_t* srcF = ((uint16_t*)src) + (i * 4); - dest[0] = ((float)srcF[0]) / 65535.0f; - dest[1] = ((float)srcF[1]) / 65535.0f; - dest[2] = ((float)srcF[2]) / 65535.0f; - dest[3] = ((float)srcF[3]) / 65535.0f; + dest[0] = ((float)srcF[0]) / 65535.0f; + dest[1] = ((float)srcF[1]) / 65535.0f; + dest[2] = ((float)srcF[2]) / 65535.0f; + dest[3] = ((float)srcF[3]) / 65535.0f; - break; - } + break; + } - case AImgFormat::R32F: - { - float* srcF = ((float*)src) + (i*1); + case AImgFormat::R32F: + { + float* srcF = ((float*)src) + (i * 1); - dest[0] = srcF[0]; - dest[1] = srcF[0]; - dest[2] = srcF[0]; - dest[3] = 1; + dest[0] = srcF[0]; + dest[1] = srcF[0]; + dest[2] = srcF[0]; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RG32F: - { - float* srcF = ((float*)src) + (i*2); + case AImgFormat::RG32F: + { + float* srcF = ((float*)src) + (i * 2); - dest[0] = srcF[0]; - dest[1] = srcF[1]; - dest[2] = 0; - dest[3] = 1; + dest[0] = srcF[0]; + dest[1] = srcF[1]; + dest[2] = 0; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGB32F: - { - float* srcF = ((float*)src) + (i*3); + case AImgFormat::RGB32F: + { + float* srcF = ((float*)src) + (i * 3); - dest[0] = srcF[0]; - dest[1] = srcF[1]; - dest[2] = srcF[2]; - dest[3] = 1; + dest[0] = srcF[0]; + dest[1] = srcF[1]; + dest[2] = srcF[2]; + dest[3] = 1; - break; - } + break; + } - case AImgFormat::RGBA32F: - { - float* srcF = ((float*)src) + (i*4); + case AImgFormat::RGBA32F: + { + float* srcF = ((float*)src) + (i * 4); - dest[0] = srcF[0]; - dest[1] = srcF[1]; - dest[2] = srcF[2]; - dest[3] = srcF[3]; + dest[0] = srcF[0]; + dest[1] = srcF[1]; + dest[2] = srcF[2]; + dest[3] = srcF[3]; - break; - } + break; + } - default: - { - break; - } + default: + { + break; + } } } @@ -357,335 +362,390 @@ void convertFromRGBA32F(std::vector& src, void* dst, size_t i, int32_t ou { switch (outFormat) { - case AImgFormat::R8U: - { - uint8_t* dstF = ((uint8_t*)dst) + (i*1); + case AImgFormat::R8U: + { + uint8_t* dstF = ((uint8_t*)dst) + (i * 1); - dstF[0] = (uint8_t)(src[0] * 255.0f); + dstF[0] = (uint8_t)(src[0] * 255.0f); - break; - } + break; + } - case AImgFormat::RG8U: - { - uint8_t* dstF = ((uint8_t*)dst) + (i*2); + case AImgFormat::RG8U: + { + uint8_t* dstF = ((uint8_t*)dst) + (i * 2); - dstF[0] = (uint8_t)(src[0] * 255.0f); - dstF[1] = (uint8_t)(src[1] * 255.0f); + dstF[0] = (uint8_t)(src[0] * 255.0f); + dstF[1] = (uint8_t)(src[1] * 255.0f); - break; - } + break; + } - case AImgFormat::RGB8U: - { - uint8_t* dstF = ((uint8_t*)dst) + (i*3); + case AImgFormat::RGB8U: + { + uint8_t* dstF = ((uint8_t*)dst) + (i * 3); - dstF[0] = (uint8_t)(src[0] * 255.0f); - dstF[1] = (uint8_t)(src[1] * 255.0f); - dstF[2] = (uint8_t)(src[2] * 255.0f); + dstF[0] = (uint8_t)(src[0] * 255.0f); + dstF[1] = (uint8_t)(src[1] * 255.0f); + dstF[2] = (uint8_t)(src[2] * 255.0f); - break; - } + break; + } - case AImgFormat::RGBA8U: - { - uint8_t* dstF = ((uint8_t*)dst) + (i*4); + case AImgFormat::RGBA8U: + { + uint8_t* dstF = ((uint8_t*)dst) + (i * 4); - dstF[0] = (uint8_t)(src[0] * 255.0f); - dstF[1] = (uint8_t)(src[1] * 255.0f); - dstF[2] = (uint8_t)(src[2] * 255.0f); - dstF[3] = (uint8_t)(src[3] * 255.0f); + dstF[0] = (uint8_t)(src[0] * 255.0f); + dstF[1] = (uint8_t)(src[1] * 255.0f); + dstF[2] = (uint8_t)(src[2] * 255.0f); + dstF[3] = (uint8_t)(src[3] * 255.0f); - break; - } + break; + } - #ifdef HAVE_EXR - case AImgFormat::R16F: - { - half* dstF = ((half*)dst) + (i*1); +#ifdef HAVE_EXR + case AImgFormat::R16F: + { + half* dstF = ((half*)dst) + (i * 1); - dstF[0] = src[0]; + dstF[0] = src[0]; - break; - } + break; + } - case AImgFormat::RG16F: - { - half* dstF = ((half*)dst) + (i*2); + case AImgFormat::RG16F: + { + half* dstF = ((half*)dst) + (i * 2); - dstF[0] = src[0]; - dstF[1] = src[1]; + dstF[0] = src[0]; + dstF[1] = src[1]; - break; - } + break; + } - case AImgFormat::RGB16F: - { - half* dstF = ((half*)dst) + (i*3); + case AImgFormat::RGB16F: + { + half* dstF = ((half*)dst) + (i * 3); - dstF[0] = src[0]; - dstF[1] = src[1]; - dstF[2] = src[2]; + dstF[0] = src[0]; + dstF[1] = src[1]; + dstF[2] = src[2]; - break; - } + break; + } - case AImgFormat::RGBA16F: - { - half* dstF = ((half*)dst) + (i*4); + case AImgFormat::RGBA16F: + { + half* dstF = ((half*)dst) + (i * 4); - dstF[0] = src[0]; - dstF[1] = src[1]; - dstF[2] = src[2]; - dstF[3] = src[3]; + dstF[0] = src[0]; + dstF[1] = src[1]; + dstF[2] = src[2]; + dstF[3] = src[3]; - break; - } - #endif + break; + } +#endif - case AImgFormat::R16U: - { - uint16_t* dstF = ((uint16_t*)dst) + (i*1); + case AImgFormat::R16U: + { + uint16_t* dstF = ((uint16_t*)dst) + (i * 1); - dstF[0] = (uint16_t)(src[0] * 65535.0f); + dstF[0] = (uint16_t)(src[0] * 65535.0f); - break; - } + break; + } - case AImgFormat::RG16U: - { - uint16_t* dstF = ((uint16_t*)dst) + (i*2); + case AImgFormat::RG16U: + { + uint16_t* dstF = ((uint16_t*)dst) + (i * 2); - dstF[0] = (uint16_t)(src[0] * 65535.0f); - dstF[1] = (uint16_t)(src[1] * 65535.0f); + dstF[0] = (uint16_t)(src[0] * 65535.0f); + dstF[1] = (uint16_t)(src[1] * 65535.0f); - break; - } + break; + } - case AImgFormat::RGB16U: - { - uint16_t* dstF = ((uint16_t*)dst) + (i*3); + case AImgFormat::RGB16U: + { + uint16_t* dstF = ((uint16_t*)dst) + (i * 3); - dstF[0] = (uint16_t)(src[0] * 65535.0f); - dstF[1] = (uint16_t)(src[1] * 65535.0f); - dstF[2] = (uint16_t)(src[2] * 65535.0f); + dstF[0] = (uint16_t)(src[0] * 65535.0f); + dstF[1] = (uint16_t)(src[1] * 65535.0f); + dstF[2] = (uint16_t)(src[2] * 65535.0f); - break; - } + break; + } - case AImgFormat::RGBA16U: - { - uint16_t* dstF = ((uint16_t*)dst) + (i*4); + case AImgFormat::RGBA16U: + { + uint16_t* dstF = ((uint16_t*)dst) + (i * 4); - dstF[0] = (uint16_t)(src[0] * 65535.0f); - dstF[1] = (uint16_t)(src[1] * 65535.0f); - dstF[2] = (uint16_t)(src[2] * 65535.0f); - dstF[3] = (uint16_t)(src[3] * 65535.0f); + dstF[0] = (uint16_t)(src[0] * 65535.0f); + dstF[1] = (uint16_t)(src[1] * 65535.0f); + dstF[2] = (uint16_t)(src[2] * 65535.0f); + dstF[3] = (uint16_t)(src[3] * 65535.0f); - break; - } + break; + } - case AImgFormat::R32F: - { - float* dstF = ((float*)dst) + (i*1); + case AImgFormat::R32F: + { + float* dstF = ((float*)dst) + (i * 1); - dstF[0] = src[0]; + dstF[0] = src[0]; - break; - } + break; + } - case AImgFormat::RG32F: - { - float* dstF = ((float*)dst) + (i*2); + case AImgFormat::RG32F: + { + float* dstF = ((float*)dst) + (i * 2); - dstF[0] = src[0]; - dstF[1] = src[1]; + dstF[0] = src[0]; + dstF[1] = src[1]; - break; - } + break; + } - case AImgFormat::RGB32F: - { - float* dstF = ((float*)dst) + (i*3); + case AImgFormat::RGB32F: + { + float* dstF = ((float*)dst) + (i * 3); - dstF[0] = src[0]; - dstF[1] = src[1]; - dstF[2] = src[2]; + dstF[0] = src[0]; + dstF[1] = src[1]; + dstF[2] = src[2]; - break; - } + break; + } - case AImgFormat::RGBA32F: - { - float* dstF = ((float*)dst) + (i*4); + case AImgFormat::RGBA32F: + { + float* dstF = ((float*)dst) + (i * 4); - dstF[0] = src[0]; - dstF[1] = src[1]; - dstF[2] = src[2]; - dstF[3] = src[3]; + dstF[0] = src[0]; + dstF[1] = src[1]; + dstF[2] = src[2]; + dstF[3] = src[3]; - break; - } + break; + } - default: + default: + { + break; + } + } +} + +int32_t AIGetBitDepth(int32_t format) +{ + AImgFormat bitDepths[] = { + AImgFormat::_8BITS, + AImgFormat::_16BITS, + AImgFormat::_32BITS }; + + for (const AImgFormat &flag : bitDepths) + { + if (format & flag) { - break; + return flag; } } + + delete[](&bitDepths); + + return AImgFormat::INVALID_FORMAT; +} + +int32_t AIChangeBitDepth(int32_t format, int32_t newBitDepth) +{ + if (newBitDepth != AImgFormat::_8BITS + && newBitDepth != AImgFormat::_16BITS + && newBitDepth != AImgFormat::_32BITS) + { + return AImgFormat::INVALID_FORMAT; + } + + int32_t oldBitDepth = AIGetBitDepth(format); + + // Remove old flag and add new one + int32_t newFormat = format; + if (oldBitDepth != AImgFormat::INVALID_FORMAT) + { + newFormat = format & ~oldBitDepth; + } + + newFormat |= newBitDepth; + + // Add float flag if the new bit depth is 32 + if (newBitDepth == AImgFormat::_32BITS) + { + newFormat |= AImgFormat::FLOAT_FORMAT; + } + + // Remove float flag if the new bit depth is 8 + else if (newBitDepth == AImgFormat::_8BITS) + { + newFormat &= ~AImgFormat::FLOAT_FORMAT; + } + + return (AImgFormat)newFormat; } void AIGetFormatDetails(int32_t format, int32_t* numChannels, int32_t* bytesPerChannel, int32_t* floatOrInt) { - switch(format) + switch (format) { - case AImgFormat::R8U: - { - *numChannels = 1; - *bytesPerChannel = 1; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::R8U: + { + *numChannels = 1; + *bytesPerChannel = 1; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::RG8U: - { - *numChannels = 2; - *bytesPerChannel = 1; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::RG8U: + { + *numChannels = 2; + *bytesPerChannel = 1; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::RGB8U: - { - *numChannels = 3; - *bytesPerChannel = 1; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::RGB8U: + { + *numChannels = 3; + *bytesPerChannel = 1; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::RGBA8U: - { - *numChannels = 4; - *bytesPerChannel = 1; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::RGBA8U: + { + *numChannels = 4; + *bytesPerChannel = 1; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::R16F: - { - *numChannels = 1; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::R16F: + { + *numChannels = 1; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::RG16F: - { - *numChannels = 2; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::RG16F: + { + *numChannels = 2; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::RGB16F: - { - *numChannels = 3; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::RGB16F: + { + *numChannels = 3; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::RGBA16F: - { - *numChannels = 4; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::RGBA16F: + { + *numChannels = 4; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::R16U: - { - *numChannels = 1; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::R16U: + { + *numChannels = 1; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::RG16U: - { - *numChannels = 2; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::RG16U: + { + *numChannels = 2; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::RGB16U: - { - *numChannels = 3; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::RGB16U: + { + *numChannels = 3; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::RGBA16U: - { - *numChannels = 4; - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - break; - } + case AImgFormat::RGBA16U: + { + *numChannels = 4; + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + break; + } - case AImgFormat::R32F: - { - *numChannels = 1; - *bytesPerChannel = 4; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::R32F: + { + *numChannels = 1; + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::RG32F: - { - *numChannels = 2; - *bytesPerChannel = 4; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::RG32F: + { + *numChannels = 2; + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::RGB32F: - { - *numChannels = 3; - *bytesPerChannel = 4; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::RGB32F: + { + *numChannels = 3; + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - case AImgFormat::RGBA32F: - { - *numChannels = 4; - *bytesPerChannel = 4; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - break; - } + case AImgFormat::RGBA32F: + { + *numChannels = 4; + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + break; + } - default: - { - *numChannels = -1; - *bytesPerChannel = -1; - *floatOrInt = AImgFloatOrIntType::FITYPE_UNKNOWN; - break; - } + default: + { + *numChannels = -1; + *bytesPerChannel = -1; + *floatOrInt = AImgFloatOrIntType::FITYPE_UNKNOWN; + break; + } } } int32_t AImgConvertFormat(void* src, void* dest, int32_t width, int32_t height, int32_t inFormat, int32_t outFormat) { - #ifndef HAVE_EXR - if(inFormat == AImgFormat::R16F || inFormat == AImgFormat::RG16F || inFormat == AImgFormat::RGB16F || inFormat == AImgFormat::RGBA16F || - outFormat == AImgFormat::R16F || outFormat == AImgFormat::RG16F || outFormat == AImgFormat::RGB16F || outFormat == AImgFormat::RGBA16F) +#ifndef HAVE_EXR + if (inFormat == AImgFormat::R16F || inFormat == AImgFormat::RG16F || inFormat == AImgFormat::RGB16F || inFormat == AImgFormat::RGBA16F || + outFormat == AImgFormat::R16F || outFormat == AImgFormat::RG16F || outFormat == AImgFormat::RGB16F || outFormat == AImgFormat::RGBA16F) { AISetLastErrorDetails("Bad format requested, 16 bit float formats not available when compiled without EXR support"); return AImgErrorCode::AIMG_CONVERSION_FAILED_BAD_FORMAT; } - #endif +#endif std::vector scratch(4); @@ -695,12 +755,12 @@ int32_t AImgConvertFormat(void* src, void* dest, int32_t width, int32_t height, AIGetFormatDetails(outFormat, &_, &_, &floatOrInt); bool outIsFloat = floatOrInt == AImgFloatOrIntType::FITYPE_FLOAT; - for(int32_t i = 0; i < width*height; i++) + for (int32_t i = 0; i < width*height; i++) { convertToRGBA32F(src, scratch, i, inFormat); // clamp to 0-1 range - if(inIsFloat && !outIsFloat) + if (inIsFloat && !outIsFloat) { scratch[0] = std::min(1.0f, std::max(0.0f, scratch[0])); scratch[1] = std::min(1.0f, std::max(0.0f, scratch[1])); @@ -714,9 +774,14 @@ int32_t AImgConvertFormat(void* src, void* dest, int32_t width, int32_t height, return AImgErrorCode::AIMG_SUCCESS; } -int32_t AImgGetWhatFormatWillBeWrittenForData(int32_t fileFormat, int32_t inputFormat) +bool AImgIsFormatSupported(int32_t fileFormat, int32_t outputFormat) { - return loaders[fileFormat]->getWhatFormatWillBeWrittenForData(inputFormat); + return loaders[fileFormat]->isFormatSupported(outputFormat); +} + +int32_t AImgGetWhatFormatWillBeWrittenForData(int32_t fileFormat, int32_t inputFormat, int32_t outputFormat) +{ + return loaders[fileFormat]->getWhatFormatWillBeWrittenForData(inputFormat, outputFormat); } struct SimpleMemoryCallbackData @@ -731,11 +796,11 @@ struct SimpleMemoryCallbackData int32_t CALLCONV simpleMemoryReadCallback(void* callbackData, uint8_t* dest, int32_t count) { auto data = (SimpleMemoryCallbackData*)callbackData; - + int32_t toWrite = count; int32_t end = data->currentPos + count; - - if(end > data->size) + + if (end > data->size) toWrite = data->size - data->currentPos; memcpy(dest, data->buffer + data->currentPos, toWrite); @@ -752,7 +817,7 @@ void CALLCONV simpleMemoryWriteCallback(void* callbackData, const uint8_t* src, int32_t toWrite = count; int32_t end = data->currentPos + count; - if(end > data->size) + if (end > data->size) toWrite = data->size - data->currentPos; memcpy(data->buffer + data->currentPos, src, toWrite); @@ -767,11 +832,11 @@ void CALLCONV simpleMemoryResizableWriteCallback(void* callbackData, const uint8 int32_t toWrite = count; int32_t end = data->currentPos + count; - if(end > data->size) + if (end > data->size) { data->vecBuffer->resize(end); data->buffer = &data->vecBuffer->operator[](0); - data->size = data->vecBuffer->size(); + data->size = (int32_t)data->vecBuffer->size(); } memcpy(data->buffer + data->currentPos, src, toWrite); @@ -779,7 +844,6 @@ void CALLCONV simpleMemoryResizableWriteCallback(void* callbackData, const uint8 data->currentPos += toWrite; } - int32_t CALLCONV simpleMemoryTellCallback(void* callbackData) { auto data = (SimpleMemoryCallbackData*)callbackData; @@ -796,7 +860,7 @@ void AIGetSimpleMemoryBufferCallbacks(ReadCallback* readCallback, WriteCallback* { *readCallback = &simpleMemoryReadCallback; *writeCallback = &simpleMemoryWriteCallback; - *tellCallback = &simpleMemoryTellCallback; + *tellCallback = &simpleMemoryTellCallback; *seekCallback = &simpleMemorySeekCallback; auto data = new SimpleMemoryCallbackData(); @@ -815,7 +879,7 @@ void AIGetResizableMemoryBufferCallbacks(ReadCallback* readCallback, WriteCallba *seekCallback = &simpleMemorySeekCallback; auto data = new SimpleMemoryCallbackData(); - data->size = vec->size(); + data->size = (int32_t)vec->size(); data->buffer = &vec->operator[](0); data->currentPos = 0; data->vecBuffer = vec; @@ -832,4 +896,4 @@ void AIDestroySimpleMemoryBufferCallbacks(ReadCallback readCallback, WriteCallba auto data = (SimpleMemoryCallbackData*)callbackData; delete data; -} +} \ No newline at end of file diff --git a/src_c/AIL.h b/src_c/AIL.h index a20b914..3da113b 100644 --- a/src_c/AIL.h +++ b/src_c/AIL.h @@ -4,109 +4,120 @@ #include #ifdef __cplusplus -extern "C" +extern "C" { #endif #ifdef WIN32 - #define CALLCONV __stdcall +#define CALLCONV __stdcall - #ifdef IS_AIL_COMPILE - #define EXPORT_FUNC __declspec(dllexport) - #else - #define EXPORT_FUNC __declspec(dllimport) - #endif +#ifdef IS_AIL_COMPILE +#define EXPORT_FUNC __declspec(dllexport) #else - #define CALLCONV - #define EXPORT_FUNC +#define EXPORT_FUNC __declspec(dllimport) +#endif +#else +#define CALLCONV +#define EXPORT_FUNC #endif -/////////////////////// -// Callback typedefs // -/////////////////////// -typedef int32_t (CALLCONV *ReadCallback) (void* callbackData, uint8_t* dest, int32_t count); -typedef void (CALLCONV *WriteCallback) (void* callbackData, const uint8_t* src, int32_t count); -typedef int32_t (CALLCONV *TellCallback) (void* callbackData); -typedef void (CALLCONV *SeekCallback) (void* callbackData, int32_t pos); - - -//////////////// -// Core enums // -//////////////// - -// format is [channels][bits per channel][U/F] -// U means unsigned normalised, so eg 8U maps integer vals 0-255 to float range 0-1, F means a normal float value -enum AImgFormat -{ - INVALID_FORMAT = -1, - - R8U = 0, - RG8U = 1, - RGB8U = 2, - RGBA8U = 3, - - R16U = 4, - RG16U = 5, - RGB16U = 6, - RGBA16U = 7, - - R16F = 8, - RG16F = 9, - RGB16F = 10, - RGBA16F = 11, - - R32F = 12, - RG32F = 13, - RGB32F = 14, - RGBA32F = 15 -}; - -enum AImgErrorCode -{ - AIMG_SUCCESS = 0, - AIMG_UNSUPPORTED_FILETYPE = -1, - AIMG_LOAD_FAILED_EXTERNAL = -2, // load failed in an external library - AIMG_LOAD_FAILED_INTERNAL = -3, // load failed inside ArtomatixImageLoader - AIMG_CONVERSION_FAILED_BAD_FORMAT = -4, - AIMG_WRITE_FAILED_EXTERNAL = -5, - AIMG_WRITE_FAILED_INTERNAL = -6, - AIMG_LOAD_FAILED_UNSUPPORTED_TIFF = -7, - AIMG_OPEN_FAILED_EMPTY_INPUT = -8, - AIMG_INVALID_ENCODE_ARGS = -9 -}; - -enum AImgFileFormat -{ - UNKNOWN_IMAGE_FORMAT = -1, - EXR_IMAGE_FORMAT = 1, - PNG_IMAGE_FORMAT = 2, - JPEG_IMAGE_FORMAT = 3, - TGA_IMAGE_FORMAT = 4, - TIFF_IMAGE_FORMAT = 5 -}; - -enum AImgFloatOrIntType -{ - FITYPE_UNKNOWN = -1, - FITYPE_FLOAT = 0, - FITYPE_INT = 1 -}; - -///////////////////////////// -// Encoding option structs // -///////////////////////////// -// // -// - All option structs // -// will have an int32 as // -// their first member, // -// which shall be required // -// to be set to the // -// AImgFileFormat code for // -// that file format. // -///////////////////////////// - - -// These defines copied from libpng's png.h + /////////////////////// + // Callback typedefs // + /////////////////////// + typedef int32_t(CALLCONV *ReadCallback) (void* callbackData, uint8_t* dest, int32_t count); + typedef void (CALLCONV *WriteCallback) (void* callbackData, const uint8_t* src, int32_t count); + typedef int32_t(CALLCONV *TellCallback) (void* callbackData); + typedef void (CALLCONV *SeekCallback) (void* callbackData, int32_t pos); + + //////////////// + // Core enums // + //////////////// + + // format is [channels][bits per channel][U/F] + // U means unsigned normalised, so eg 8U maps integer vals 0-255 to float range 0-1, F means a normal float value + enum AImgFormat + { + INVALID_FORMAT = -1, + + _8BITS = 1 << 0, + _16BITS = 1 << 5, + _32BITS = 1 << 6, + + R = 1 << 1, + RG = 1 << 2, + RGB = 1 << 3, + RGBA = 1 << 4, + + FLOAT_FORMAT = 1 << 7, + + R8U = R | _8BITS, + RG8U = RG | _8BITS, + RGB8U = RGB | _8BITS, + RGBA8U = RGBA | _8BITS, + + R16U = R | _16BITS, + RG16U = RG | _16BITS, + RGB16U = RGB | _16BITS, + RGBA16U = RGBA | _16BITS, + + R16F = R | _16BITS | FLOAT_FORMAT, + RG16F = RG | _16BITS | FLOAT_FORMAT, + RGB16F = RGB | _16BITS | FLOAT_FORMAT, + RGBA16F = RGBA | _16BITS | FLOAT_FORMAT, + + R32F = R | _32BITS | FLOAT_FORMAT, + RG32F = RG | _32BITS | FLOAT_FORMAT, + RGB32F = RGB | _32BITS | FLOAT_FORMAT, + RGBA32F = RGBA | _32BITS | FLOAT_FORMAT + }; + + enum AImgErrorCode + { + AIMG_SUCCESS = 0, + AIMG_UNSUPPORTED_FILETYPE = -1, + AIMG_LOAD_FAILED_EXTERNAL = -2, // load failed in an external library + AIMG_LOAD_FAILED_INTERNAL = -3, // load failed inside ArtomatixImageLoader + AIMG_CONVERSION_FAILED_BAD_FORMAT = -4, + AIMG_WRITE_FAILED_EXTERNAL = -5, + AIMG_WRITE_FAILED_INTERNAL = -6, + AIMG_LOAD_FAILED_UNSUPPORTED_TIFF = -7, + AIMG_OPEN_FAILED_EMPTY_INPUT = -8, + AIMG_INVALID_ENCODE_ARGS = -9, + AIMG_WRITE_NOT_SUPPORTED_FOR_FORMAT = -10 + }; + + enum AImgFileFormat + { + UNKNOWN_IMAGE_FORMAT = -1, + EXR_IMAGE_FORMAT = 1, + PNG_IMAGE_FORMAT = 2, + JPEG_IMAGE_FORMAT = 3, + TGA_IMAGE_FORMAT = 4, + TIFF_IMAGE_FORMAT = 5, + HDR_IMAGE_FORMAT = 6 + }; + + enum AImgFloatOrIntType + { + FITYPE_UNKNOWN = -1, + FITYPE_FLOAT = 0, + FITYPE_INT = 1 + }; + + ///////////////////////////// + // Encoding option structs // + ///////////////////////////// + // // + // - All option structs // + // will have an int32 as // + // their first member, // + // which shall be required // + // to be set to the // + // AImgFileFormat code for // + // that file format. // + ///////////////////////////// + + // These defines copied from libpng's png.h #define AIL_PNG_NO_FILTERS 0x00 #define AIL_PNG_FILTER_NONE 0x08 #define AIL_PNG_FILTER_SUB 0x10 @@ -115,53 +126,56 @@ enum AImgFloatOrIntType #define AIL_PNG_FILTER_PAETH 0x80 #define AIL_PNG_ALL_FILTERS (AIL_PNG_FILTER_NONE | AIL_PNG_FILTER_SUB | AIL_PNG_FILTER_UP | AIL_PNG_FILTER_AVG | AIL_PNG_FILTER_PAETH) -struct PngEncodingOptions -{ - int32_t type; - int32_t compressionLevel; // Used with png_set_compression_level() - int32_t filter; // Used with png_set_filter(), set to some combination of AIL_PNG_ flag defines from above. -}; + struct PngEncodingOptions + { + int32_t type; + int32_t compressionLevel; // Used with png_set_compression_level() + int32_t filter; // Used with png_set_filter(), set to some combination of AIL_PNG_ flag defines from above. + }; -////////////////////////// -// Public API functions // -////////////////////////// + ////////////////////////// + // Public API functions // + ////////////////////////// -typedef void* AImgHandle; + typedef void* AImgHandle; -EXPORT_FUNC const char* AImgGetErrorDetails(AImgHandle img); + EXPORT_FUNC const char* AImgGetErrorDetails(AImgHandle img); -// detectedFileFormat will be set to a member from AImgFileFormat if non-null, otherwise it is ignored. -EXPORT_FUNC int32_t AImgOpen(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, AImgHandle* imgPtr, int32_t* detectedFileFormat); -EXPORT_FUNC void AImgClose(AImgHandle img); + // detectedFileFormat will be set to a member from AImgFileFormat if non-null, otherwise it is ignored. + EXPORT_FUNC int32_t AImgOpen(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, AImgHandle* imgPtr, int32_t* detectedFileFormat); + EXPORT_FUNC void AImgClose(AImgHandle img); -EXPORT_FUNC int32_t AImgGetInfo(AImgHandle img, int32_t* width, int32_t* height, int32_t* numChannels, int32_t* bytesPerChannel, int32_t* floatOrInt, int32_t* decodedImgFormat, uint32_t *colourProfileLen); -EXPORT_FUNC int32_t AImgGetColourProfile(AImgHandle img, char* profileName, uint8_t* colourProfile, uint32_t *colourProfileLen); -EXPORT_FUNC int32_t AImgDecodeImage(AImgHandle img, void* destBuffer, int32_t forceImageFormat); -EXPORT_FUNC int32_t AImgInitialise(); -EXPORT_FUNC void AImgCleanUp(); + EXPORT_FUNC int32_t AImgGetInfo(AImgHandle img, int32_t* width, int32_t* height, int32_t* numChannels, int32_t* bytesPerChannel, int32_t* floatOrInt, int32_t* decodedImgFormat, uint32_t *colourProfileLen); + EXPORT_FUNC int32_t AImgGetColourProfile(AImgHandle img, char* profileName, uint8_t* colourProfile, uint32_t *colourProfileLen); + EXPORT_FUNC int32_t AImgDecodeImage(AImgHandle img, void* destBuffer, int32_t forceImageFormat); + EXPORT_FUNC int32_t AImgInitialise(); + EXPORT_FUNC void AImgCleanUp(); -EXPORT_FUNC void AIGetFormatDetails(int32_t format, int32_t* numChannels, int32_t* bytesPerChannel, int32_t* floatOrInt); -EXPORT_FUNC int32_t AImgConvertFormat(void* src, void* dest, int32_t width, int32_t height, int32_t inFormat, int32_t outFormat); + EXPORT_FUNC int32_t AIGetBitDepth(int32_t format); + EXPORT_FUNC int32_t AIChangeBitDepth(int32_t format, int32_t newBitDepth); + EXPORT_FUNC void AIGetFormatDetails(int32_t format, int32_t* numChannels, int32_t* bytesPerChannel, int32_t* floatOrInt); + EXPORT_FUNC int32_t AImgConvertFormat(void* src, void* dest, int32_t width, int32_t height, int32_t inFormat, int32_t outFormat); -EXPORT_FUNC int32_t AImgGetWhatFormatWillBeWrittenForData(int32_t fileFormat, int32_t inputFormat); + EXPORT_FUNC bool AImgIsFormatSupported(int32_t fileFormat, int32_t outputFormat); -EXPORT_FUNC AImgHandle AImgGetAImg(int32_t fileFormat); + EXPORT_FUNC int32_t AImgGetWhatFormatWillBeWrittenForData(int32_t fileFormat, int32_t inputFormat, int32_t outputFormat); -// encodingOptions should be one of the encoding option structs detailed in the section above. It shoudl be the struct that corresponds to the image format being written. -EXPORT_FUNC int32_t AImgWriteImage(AImgHandle imgH, void* data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, - WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, void* encodingOptions); + EXPORT_FUNC AImgHandle AImgGetAImg(int32_t fileFormat); -EXPORT_FUNC void AIGetSimpleMemoryBufferCallbacks(ReadCallback* readCallback, WriteCallback* writeCallback, TellCallback* tellCallback, SeekCallback* seekCallback, void** callbackData, void* buffer, int32_t size); -EXPORT_FUNC void AIDestroySimpleMemoryBufferCallbacks(ReadCallback readCallback, WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData); + // encodingOptions should be one of the encoding option structs detailed in the section above. It shoudl be the struct that corresponds to the image format being written. + EXPORT_FUNC int32_t AImgWriteImage(AImgHandle imgH, void* data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, void* encodingOptions); + EXPORT_FUNC void AIGetSimpleMemoryBufferCallbacks(ReadCallback* readCallback, WriteCallback* writeCallback, TellCallback* tellCallback, SeekCallback* seekCallback, void** callbackData, void* buffer, int32_t size); + EXPORT_FUNC void AIDestroySimpleMemoryBufferCallbacks(ReadCallback readCallback, WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData); #ifdef __cplusplus } #endif #ifdef __cplusplus - #include - EXPORT_FUNC void AIGetResizableMemoryBufferCallbacks(ReadCallback* readCallback, WriteCallback* writeCallback, TellCallback* tellCallback, SeekCallback* seekCallback, void** callbackData, std::vector* vec); +#include +EXPORT_FUNC void AIGetResizableMemoryBufferCallbacks(ReadCallback* readCallback, WriteCallback* writeCallback, TellCallback* tellCallback, SeekCallback* seekCallback, void** callbackData, std::vector* vec); #endif #endif //ARTOMATIX_AIL_H diff --git a/src_c/CMakeLists.txt b/src_c/CMakeLists.txt index 46329f2..75dce7a 100644 --- a/src_c/CMakeLists.txt +++ b/src_c/CMakeLists.txt @@ -2,8 +2,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) include("cmake/HunterGate.cmake") HunterGate( - URL "https://github.com/Artomatix/hunter/archive/OpenEXR-test-2.tar.gz" - SHA1 "c86e9e6fde6b96fc57ac2184692baeb4d71e9fd6" + URL "https://github.com/Artomatix/hunter/archive/v43-OpenEXR.tar.gz" + SHA1 "faefc63600c5e431a59d406bbd0db743a659f1a9" LOCAL ) @@ -20,23 +20,21 @@ set(PNG_ENABLED ON CACHE BOOL "enable loading PNG files") set(JPEG_ENABLED ON CACHE BOOL "enable loading JPEG files") set(TIFF_ENABLED ON CACHE BOOL "enable loading TIFF files") set(TGA_ENABLED ON CACHE BOOL "enable loading TGA files") +set(HDR_ENABLED ON CACHE BOOL "enable loading HDR files") set(BUILD_SHARE_TYPE SHARED CACHE STRING "set build type, valid values: SHARED|STATIC") set(CMAKE_POSITION_INDEPENDENT_CODE YES) add_library(AIL ${BUILD_SHARE_TYPE} - exr.cpp - exr.h - png.h - png.cpp - jpeg.h - jpeg.cpp - tga.h - tga.cpp - tiff.h - tiff.cpp - AIL.cpp - AIL.h + + exr.cpp exr.h + png.h png.cpp + jpeg.h jpeg.cpp + tga.h tga.cpp + tiff.h tiff.cpp + AIL.h AIL.cpp + hdr.h hdr.cpp + AIL_internal.h ImageLoaderBase.h extern/stb_image.h @@ -111,6 +109,10 @@ if(TIFF_ENABLED) add_definitions(-DHAVE_TIFF) endif() +if (HDR_ENABLED) + add_definitions(-DHAVE_HDR) +endif() + SET(CMAKE_DEBUG_POSTFIX "") ######### diff --git a/src_c/ImageLoaderBase.h b/src_c/ImageLoaderBase.h index 597e7d6..ec7ad11 100644 --- a/src_c/ImageLoaderBase.h +++ b/src_c/ImageLoaderBase.h @@ -17,8 +17,9 @@ namespace AImg virtual int32_t getColourProfile(char* profileName, uint8_t* colourProfile, uint32_t *colourProfileLen) = 0; virtual int32_t decodeImage(void* destBuffer, int32_t forceImageFormat) = 0; - virtual int32_t writeImage(void* data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, - WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, void* encodingOptions) = 0; + virtual int32_t writeImage(void* data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, + const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData, void* encodingOptions) = 0; const char* getErrorDetails() { @@ -52,7 +53,9 @@ namespace AImg virtual std::string getFileExtension() = 0; virtual int32_t getAImgFileFormatValue() = 0; - virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat) = 0; + virtual bool isFormatSupported(int32_t format) = 0; + + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) = 0; }; } diff --git a/src_c/cmake/Hunter/config.cmake b/src_c/cmake/Hunter/config.cmake index 76f60ec..bdefdeb 100644 --- a/src_c/cmake/Hunter/config.cmake +++ b/src_c/cmake/Hunter/config.cmake @@ -1,4 +1,4 @@ -hunter_config(OpenEXR VERSION "1.0.1" CMAKE_ARGS CMAKE_POSITION_INDEPENDENT_CODE=YES) +hunter_config(OpenEXR VERSION "1.0.2" CMAKE_ARGS CMAKE_POSITION_INDEPENDENT_CODE=YES) hunter_config(ZLIB VERSION "1.2.8-p3" CMAKE_ARGS CMAKE_POSITION_INDEPENDENT_CODE=YES) hunter_config(PNG VERSION "1.6.16-p4" CMAKE_ARGS CMAKE_POSITION_INDEPENDENT_CODE=YES) hunter_config(Jpeg VERSION "9b-p1" CMAKE_ARGS CMAKE_POSITION_INDEPENDENT_CODE=YES) diff --git a/src_c/cmake/HunterGate.cmake b/src_c/cmake/HunterGate.cmake index 99882d2..887557a 100644 --- a/src_c/cmake/HunterGate.cmake +++ b/src_c/cmake/HunterGate.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2015, Ruslan Baratov +# Copyright (c) 2013-2018, Ruslan Baratov # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ # This is a gate file to Hunter package manager. # Include this file using `include` command and add package you need, example: # -# cmake_minimum_required(VERSION 3.0) +# cmake_minimum_required(VERSION 3.2) # # include("cmake/HunterGate.cmake") # HunterGate( @@ -42,30 +42,41 @@ # * https://github.com/hunter-packages/gate/ # * https://github.com/ruslo/hunter -cmake_minimum_required(VERSION 3.0) # Minimum for Hunter +option(HUNTER_ENABLED "Enable Hunter package manager support" ON) + +if(HUNTER_ENABLED) + if(CMAKE_VERSION VERSION_LESS "3.2") + message( + FATAL_ERROR + "At least CMake version 3.2 required for Hunter dependency management." + " Update CMake or set HUNTER_ENABLED to OFF." + ) + endif() +endif() + include(CMakeParseArguments) # cmake_parse_arguments -option(HUNTER_ENABLED "Enable Hunter package manager support" ON) option(HUNTER_STATUS_PRINT "Print working status" ON) option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) +option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) set(HUNTER_WIKI "https://github.com/ruslo/hunter/wiki") function(hunter_gate_status_print) - foreach(print_message ${ARGV}) - if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) + if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) message(STATUS "[hunter] ${print_message}") - endif() - endforeach() + endforeach() + endif() endfunction() function(hunter_gate_status_debug) - foreach(print_message ${ARGV}) - if(HUNTER_STATUS_DEBUG) + if(HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) string(TIMESTAMP timestamp) message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") - endif() - endforeach() + endforeach() + endif() endfunction() function(hunter_gate_wiki wiki_page) @@ -188,20 +199,6 @@ function(hunter_gate_detect_root) ) endfunction() -macro(hunter_gate_lock dir) - if(NOT HUNTER_SKIP_LOCK) - if("${CMAKE_VERSION}" VERSION_LESS "3.2") - hunter_gate_fatal_error( - "Can't lock, upgrade to CMake 3.2 or use HUNTER_SKIP_LOCK" - WIKI "error.can.not.lock" - ) - endif() - hunter_gate_status_debug("Locking directory: ${dir}") - file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) - hunter_gate_status_debug("Lock done") - endif() -endmacro() - function(hunter_gate_download dir) string( COMPARE @@ -241,7 +238,10 @@ function(hunter_gate_download dir) set(build_dir "${dir}/Build") set(cmakelists "${dir}/CMakeLists.txt") - hunter_gate_lock("${dir}") + hunter_gate_status_debug("Locking directory: ${dir}") + file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) + hunter_gate_status_debug("Lock done") + if(EXISTS "${done_location}") # while waiting for lock other instance can do all the job hunter_gate_status_debug("File '${done_location}' found, skip install") @@ -258,7 +258,7 @@ function(hunter_gate_download dir) file( WRITE "${cmakelists}" - "cmake_minimum_required(VERSION 3.0)\n" + "cmake_minimum_required(VERSION 3.2)\n" "project(HunterDownload LANGUAGES NONE)\n" "include(ExternalProject)\n" "ExternalProject_Add(\n" @@ -269,6 +269,8 @@ function(hunter_gate_download dir) " SHA1=${HUNTER_GATE_SHA1}\n" " DOWNLOAD_DIR\n" " \"${dir}\"\n" + " TLS_VERIFY\n" + " ${HUNTER_TLS_VERIFY}\n" " SOURCE_DIR\n" " \"${dir}/Unpacked\"\n" " CONFIGURE_COMMAND\n" @@ -292,21 +294,40 @@ function(hunter_gate_download dir) # Otherwise on Visual Studio + MDD this will fail with error: # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") - set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") + get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") else() # 'toolchain_arg' can't be empty set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") endif() + string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) + if(no_make) + set(make_arg "") + else() + # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM + set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") + endif() + execute_process( - COMMAND "${CMAKE_COMMAND}" "-H${dir}" "-B${build_dir}" "-G${CMAKE_GENERATOR}" "${toolchain_arg}" + COMMAND + "${CMAKE_COMMAND}" + "-H${dir}" + "-B${build_dir}" + "-G${CMAKE_GENERATOR}" + "${toolchain_arg}" + ${make_arg} WORKING_DIRECTORY "${dir}" RESULT_VARIABLE download_result ${logging_params} ) if(NOT download_result EQUAL 0) - hunter_gate_internal_error("Configure project failed") + hunter_gate_internal_error( + "Configure project failed." + "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" + "In directory ${dir}" + ) endif() hunter_gate_status_print( @@ -351,6 +372,17 @@ macro(HunterGate) # Empty function to avoid error "unknown function" function(hunter_add_package) endfunction() + + set( + _hunter_gate_disabled_mode_dir + "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" + ) + if(EXISTS "${_hunter_gate_disabled_mode_dir}") + hunter_gate_status_debug( + "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" + ) + list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") + endif() elseif(_hunter_gate_done) hunter_gate_status_debug("Secondary HunterGate (use old settings)") hunter_gate_self( @@ -361,7 +393,7 @@ macro(HunterGate) ) include("${_hunter_self}/cmake/Hunter") else() - set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_LIST_DIR}") + set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) if(_have_project_name) diff --git a/src_c/exr.cpp b/src_c/exr.cpp index ebb1a44..2aa2ef1 100644 --- a/src_c/exr.cpp +++ b/src_c/exr.cpp @@ -18,486 +18,491 @@ namespace AImg { -class CallbackIStream : public Imf::IStream -{ - public: - CallbackIStream(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) : IStream("") + class CallbackIStream : public Imf::IStream { - mReadCallback = readCallback; - mTellCallback = tellCallback; - mSeekCallback = seekCallback; - mCallbackData = callbackData; - } + public: + CallbackIStream(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) : IStream("") + { + mReadCallback = readCallback; + mTellCallback = tellCallback; + mSeekCallback = seekCallback; + mCallbackData = callbackData; + } - virtual bool read(char c[], int n) - { - return mReadCallback(mCallbackData, (uint8_t *)c, n) == n; - } + virtual bool read(char c[], int n) + { + return mReadCallback(mCallbackData, (uint8_t *)c, n) == n; + } - virtual uint64_t tellg() - { - return mTellCallback(mCallbackData); - } + virtual uint64_t tellg() + { + return mTellCallback(mCallbackData); + } - virtual void seekg(uint64_t pos) - { - mSeekCallback(mCallbackData, pos); - } + virtual void seekg(uint64_t pos) + { + mSeekCallback(mCallbackData, (int32_t)pos); + } - virtual void clear() - { - } + virtual void clear() + { + } - ReadCallback mReadCallback; - TellCallback mTellCallback; - SeekCallback mSeekCallback; - void *mCallbackData; -}; + ReadCallback mReadCallback; + TellCallback mTellCallback; + SeekCallback mSeekCallback; + void *mCallbackData; + }; -class CallbackOStream : public Imf::OStream -{ - public: - CallbackOStream(WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) : OStream("") + class CallbackOStream : public Imf::OStream { - mWriteCallback = writeCallback; - mTellCallback = tellCallback; - mSeekCallback = seekCallback; - mCallbackData = callbackData; - } + public: + CallbackOStream(WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) : OStream("") + { + mWriteCallback = writeCallback; + mTellCallback = tellCallback; + mSeekCallback = seekCallback; + mCallbackData = callbackData; + } - virtual void write(const char c[], int n) - { - mWriteCallback(mCallbackData, (const uint8_t *)c, n); - } + virtual void write(const char c[], int n) + { + mWriteCallback(mCallbackData, (const uint8_t *)c, n); + } - virtual uint64_t tellp() - { - return mTellCallback(mCallbackData); - } + virtual uint64_t tellp() + { + return mTellCallback(mCallbackData); + } - virtual void seekp(uint64_t pos) - { - mSeekCallback(mCallbackData, pos); - } + virtual void seekp(uint64_t pos) + { + mSeekCallback(mCallbackData, (int32_t)pos); + } - virtual void clear() - { - } + virtual void clear() + { + } - WriteCallback mWriteCallback; - TellCallback mTellCallback; - SeekCallback mSeekCallback; - void *mCallbackData; -}; + WriteCallback mWriteCallback; + TellCallback mTellCallback; + SeekCallback mSeekCallback; + void *mCallbackData; + }; -int32_t ExrImageLoader::initialise() -{ - try + int32_t ExrImageLoader::initialise() { - Imf::staticInitialize(); + try + { + Imf::staticInitialize(); - return AImgErrorCode::AIMG_SUCCESS; - } - catch (const std::exception &e) - { - return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; + return AImgErrorCode::AIMG_SUCCESS; + } + catch (const std::exception) + { + return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; + } } -} -bool ExrImageLoader::canLoadImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) -{ - int32_t startingPos = tellCallback(callbackData); - - std::vector header(4); - readCallback(callbackData, &header[0], 4); + bool ExrImageLoader::canLoadImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) + { + int32_t startingPos = tellCallback(callbackData); - seekCallback(callbackData, startingPos); + std::vector header(4); + readCallback(callbackData, &header[0], 4); - return header[0] == 0x76 && header[1] == 0x2f && header[2] == 0x31 && header[3] == 0x01; -} - -std::string ExrImageLoader::getFileExtension() -{ - return "EXR"; -} + seekCallback(callbackData, startingPos); -int32_t ExrImageLoader::getAImgFileFormatValue() -{ - return EXR_IMAGE_FORMAT; -} - -class ExrFile : public AImgBase -{ - public: - CallbackIStream *data = nullptr; - Imf::InputFile *file = nullptr; - Imath::Box2i dw; + return header[0] == 0x76 && header[1] == 0x2f && header[2] == 0x31 && header[3] == 0x01; + } - virtual ~ExrFile() + std::string ExrImageLoader::getFileExtension() { - if (data) - delete data; - if (file) - delete file; + return "EXR"; } - int32_t getDecodeFormat() + int32_t ExrImageLoader::getAImgFileFormatValue() { - bool useHalfFloat = true; - - // yes, I am actually pretty sure this is the only way to get a channel count... - int32_t channelNum = 0; - const Imf::ChannelList &channels = file->header().channels(); - for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) - { - channelNum++; - - // If any channels are UINT or FLOAT, then decode the whole thing as FLOAT - // Otherwise, if everything is HALF, then we can decode as HALF - if(it.channel().type != Imf::PixelType::HALF) - useHalfFloat = false; - } + return EXR_IMAGE_FORMAT; + } - if(channelNum <= 0) - return AImgFormat::INVALID_FORMAT; + bool isFormatSupportedByExr(int32_t format) + { + int32_t flags = format & AImgFormat::_16BITS; + int32_t fl = format & AImgFormat::_32BITS; + int32_t floatF = format & AImgFormat::FLOAT_FORMAT; - // start at the 1-channel version + offset to get the correct channel count format - int32_t format = useHalfFloat ? AImgFormat::R16F : AImgFormat::R32F; - format += (std::min(channelNum, 4) - 1); // min because we just truncate channels if we have >4 (exrs can have any number of channels) + bool isSupported = (fl || flags) && floatF; - return format; + return isSupported; } - virtual int32_t getImageInfo(int32_t *width, int32_t *height, int32_t *numChannels, int32_t *bytesPerChannel, int32_t *floatOrInt, int32_t *decodedImgFormat, uint32_t *colourProfileLen) + AImgFormat getWriteFormatExr(int32_t inputFormat, int32_t outputFormat) { - *width = dw.max.x - dw.min.x + 1; - *height = dw.max.y - dw.min.y + 1; - *decodedImgFormat = getDecodeFormat(); - if(colourProfileLen != NULL) + if (outputFormat != inputFormat + && outputFormat != AImgFormat::INVALID_FORMAT + && isFormatSupportedByExr(outputFormat)) { - *colourProfileLen = 0; + return (AImgFormat)outputFormat; } - *numChannels = 0; - - Imf::PixelType lastChannelType; - bool allChannelsSame = true; - - bool isFirstChannel = true; - - const Imf::ChannelList &channels = file->header().channels(); - for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) + if (!isFormatSupportedByExr(inputFormat)) { - (*numChannels)++; - - if (isFirstChannel) - { - isFirstChannel = false; - lastChannelType = it.channel().type; - } + // set inputBufFormat to the format with the same number of channels as inputFormat, but is 32F + int32_t bytesPerChannelTmp, numChannelsTmp, floatOrIntTmp; + AIGetFormatDetails(inputFormat, &numChannelsTmp, &bytesPerChannelTmp, &floatOrIntTmp); - if (it.channel().type != lastChannelType) - allChannelsSame = false; - } + AImgFormat res; - if (!allChannelsSame) - { - *bytesPerChannel = -1; - *floatOrInt = AImgFloatOrIntType::FITYPE_UNKNOWN; - } - else - { - if (lastChannelType == Imf::PixelType::UINT) - { - *bytesPerChannel = 4; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - } - if (lastChannelType == Imf::PixelType::FLOAT) - { - *bytesPerChannel = 4; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - } - else if (lastChannelType == Imf::PixelType::HALF) - { - *bytesPerChannel = 2; - *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; - } + if (bytesPerChannelTmp > 2) + res = AImgFormat::_32BITS; else - { - mErrorDetails = "[AImg::EXRImageLoader::EXRFile::] Invalid channel type in exr file"; - return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; - } + res = AImgFormat::_16BITS; + + return (AImgFormat)(res | AImgFormat::FLOAT_FORMAT | (1 << numChannelsTmp)); } - return AImgErrorCode::AIMG_SUCCESS; + return (AImgFormat)inputFormat; } - - virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + + class ExrFile : public AImgBase { - if(colourProfile != NULL) - { - *colourProfileLen = 0; - } - if(profileName != NULL) + public: + CallbackIStream *data = nullptr; + Imf::InputFile *file = nullptr; + Imath::Box2i dw; + + virtual ~ExrFile() { - std::strcpy(profileName, "no_profile"); + if (data) + delete data; + if (file) + delete file; } - return AImgErrorCode::AIMG_SUCCESS; - } - - virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) - { - try + int32_t getDecodeFormat() { - int32_t width = dw.max.x - dw.min.x + 1; - int32_t height = dw.max.y - dw.min.y + 1; + bool useHalfFloat = true; - int32_t decodeFormat = getDecodeFormat(); + // yes, I am actually pretty sure this is the only way to get a channel count... + int32_t channelNum = 0; + const Imf::ChannelList &channels = file->header().channels(); + for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) + { + channelNum++; - int32_t decodeFormatNumChannels, decodeFormatBytesPerChannel, decodeFormatFloatOrInt; - AIGetFormatDetails(decodeFormat, &decodeFormatNumChannels, &decodeFormatBytesPerChannel, &decodeFormatFloatOrInt); + // If any channels are UINT or FLOAT, then decode the whole thing as FLOAT + // Otherwise, if everything is HALF, then we can decode as HALF + if (it.channel().type != Imf::PixelType::HALF) + useHalfFloat = false; + } + + if (channelNum <= 0) + return AImgFormat::INVALID_FORMAT; + // start at the 1-channel version + offset to get the correct channel count format + int32_t format = (useHalfFloat ? AImgFormat::_16BITS : AImgFormat::_32BITS) | AImgFormat::FLOAT_FORMAT; + format |= (AImgFormat::R << (std::min(channelNum, 4) - 1)); // min because we just truncate channels if we have >4 (exrs can have any number of channels) - void *destBuffer = realDestBuffer; + return format; + } - std::vector convertTmpBuffer(0); - if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + virtual int32_t getImageInfo(int32_t *width, int32_t *height, int32_t *numChannels, int32_t *bytesPerChannel, int32_t *floatOrInt, int32_t *decodedImgFormat, uint32_t *colourProfileLen) + { + *width = dw.max.x - dw.min.x + 1; + *height = dw.max.y - dw.min.y + 1; + *decodedImgFormat = getDecodeFormat(); + if (colourProfileLen != NULL) { - convertTmpBuffer.resize(width * height * decodeFormatBytesPerChannel * decodeFormatNumChannels); - destBuffer = &convertTmpBuffer[0]; + *colourProfileLen = 0; } - std::vector allChannelNames; - bool isRgba = true; + *numChannels = 0; + + Imf::PixelType lastChannelType; + bool allChannelsSame = true; + + bool isFirstChannel = true; const Imf::ChannelList &channels = file->header().channels(); for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) { - std::string name = it.name(); - allChannelNames.push_back(it.name()); - if (name != "R" && name != "G" && name != "B" && name != "A") - isRgba = false; - } + (*numChannels)++; - std::vector usedChannelNames; + if (isFirstChannel) + { + isFirstChannel = false; + lastChannelType = it.channel().type; + } - // ensure RGBA byte order, when loading an rgba image - if (isRgba) + if (it.channel().type != lastChannelType) + allChannelsSame = false; + } + + if (!allChannelsSame) { - if (std::find(allChannelNames.begin(), allChannelNames.end(), "R") != allChannelNames.end()) - usedChannelNames.push_back("R"); - if (std::find(allChannelNames.begin(), allChannelNames.end(), "G") != allChannelNames.end()) - usedChannelNames.push_back("G"); - if (std::find(allChannelNames.begin(), allChannelNames.end(), "B") != allChannelNames.end()) - usedChannelNames.push_back("B"); - if (std::find(allChannelNames.begin(), allChannelNames.end(), "A") != allChannelNames.end()) - usedChannelNames.push_back("A"); + *bytesPerChannel = -1; + *floatOrInt = AImgFloatOrIntType::FITYPE_UNKNOWN; } - // otherwise just whack em in in order else { - for (uint32_t i = 0; i < allChannelNames.size(); i++) + if (lastChannelType == Imf::PixelType::UINT) { - if (usedChannelNames.size() >= 4) - break; - - if (std::find(usedChannelNames.begin(), usedChannelNames.end(), allChannelNames[i]) == usedChannelNames.end()) - usedChannelNames.push_back(allChannelNames[i]); + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; } - } - - Imf::FrameBuffer frameBuffer; - for (uint32_t i = 0; i < usedChannelNames.size(); i++) - { - if(decodeFormatBytesPerChannel == 4) + if (lastChannelType == Imf::PixelType::FLOAT) { - frameBuffer.insert(usedChannelNames[i], - Imf::Slice(Imf::FLOAT, - ((char *)destBuffer) + i * sizeof(float), - sizeof(float) * usedChannelNames.size(), - sizeof(float) * width * usedChannelNames.size(), - 1, 1, - 0.0)); + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; } - else if(decodeFormatBytesPerChannel == 2) + else if (lastChannelType == Imf::PixelType::HALF) { - frameBuffer.insert(usedChannelNames[i], - Imf::Slice(Imf::HALF, - ((char *)destBuffer) + i * sizeof(half), - sizeof(half) * usedChannelNames.size(), - sizeof(half) * width * usedChannelNames.size(), - 1, 1, - 0.0)); + *bytesPerChannel = 2; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; } else { - mErrorDetails = "[AImg::EXRImageLoader::EXRFile::] invalid decodeFormatBytesPerChannel"; + mErrorDetails = "[AImg::EXRImageLoader::EXRFile::] Invalid channel type in exr file"; return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; } } - file->setFrameBuffer(frameBuffer); - file->readPixels(dw.min.y, dw.max.y); + return AImgErrorCode::AIMG_SUCCESS; + } - if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + { + if (colourProfile != NULL) { - int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, width, height, decodeFormat, forceImageFormat); - if (err != AImgErrorCode::AIMG_SUCCESS) - return err; + *colourProfileLen = 0; + } + if (profileName != NULL) + { + std::strcpy(profileName, "no_profile"); } return AImgErrorCode::AIMG_SUCCESS; } - catch (const std::exception &e) - { - mErrorDetails = std::string("[AImg::EXRImageLoader::EXRFile::] ") + e.what(); - return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; - } - } - virtual int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) - { - try + virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) { - data = new CallbackIStream(readCallback, tellCallback, seekCallback, callbackData); - file = new Imf::InputFile(*data); - dw = file->header().dataWindow(); + try + { + int32_t width = dw.max.x - dw.min.x + 1; + int32_t height = dw.max.y - dw.min.y + 1; - return AImgErrorCode::AIMG_SUCCESS; - } - catch (const std::exception &e) - { - mErrorDetails = std::string("[AImg::EXRImageLoader::EXRFile::] ") + e.what(); - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } - } + int32_t decodeFormat = getDecodeFormat(); - int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, - WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void *encodingOptions) - { - AIL_UNUSED_PARAM(encodingOptions); + int32_t decodeFormatNumChannels, decodeFormatBytesPerChannel, decodeFormatFloatOrInt; + AIGetFormatDetails(decodeFormat, &decodeFormatNumChannels, &decodeFormatBytesPerChannel, &decodeFormatFloatOrInt); - try - { - std::vector reformattedDataTmp(0); + if (decodeFormatBytesPerChannel != 2 && decodeFormatBytesPerChannel != 4) + { + mErrorDetails = "[AImg::EXRImageLoader::EXRFile::] invalid decodeFormatBytesPerChannel"; + return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; + } - void *inputBuf = data; - AImgFormat inputBufFormat = (AImgFormat)inputFormat; + char *destBuffer = (char *)realDestBuffer; - // need 32F or 16F data, so convert if necessary - if (!((inputFormat >= AImgFormat::R32F && inputFormat <= AImgFormat::RGBA32F) || (inputFormat >= AImgFormat::R16F && inputFormat <= AImgFormat::RGBA16F))) - { - // set inputBufFormat to the format with the same number of channels as inputFormat, but is 32F - int32_t bytesPerChannelTmp, numChannelsTmp, floatOrIntTmp; - AIGetFormatDetails(inputFormat, &numChannelsTmp, &bytesPerChannelTmp, &floatOrIntTmp); + std::vector convertTmpBuffer(0); + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + { + convertTmpBuffer.resize(width * height * decodeFormatBytesPerChannel * decodeFormatNumChannels); + destBuffer = (char *)convertTmpBuffer.data(); + } + + std::vector allChannelNames; + bool isRgba = true; + + const Imf::ChannelList &channels = file->header().channels(); + for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) + { + std::string name = it.name(); + allChannelNames.push_back(it.name()); + if (name != "R" && name != "G" && name != "B" && name != "A") + isRgba = false; + } + + std::vector usedChannelNames; - if(bytesPerChannelTmp > 2) - inputBufFormat = (AImgFormat)(AImgFormat::R32F + numChannelsTmp - 1); + // ensure RGBA byte order, when loading an rgba image + if (isRgba) + { + if (std::find(allChannelNames.begin(), allChannelNames.end(), "R") != allChannelNames.end()) + usedChannelNames.push_back("R"); + if (std::find(allChannelNames.begin(), allChannelNames.end(), "G") != allChannelNames.end()) + usedChannelNames.push_back("G"); + if (std::find(allChannelNames.begin(), allChannelNames.end(), "B") != allChannelNames.end()) + usedChannelNames.push_back("B"); + if (std::find(allChannelNames.begin(), allChannelNames.end(), "A") != allChannelNames.end()) + usedChannelNames.push_back("A"); + } + // otherwise just whack em in in order else - inputBufFormat = (AImgFormat)(AImgFormat::R16F + numChannelsTmp - 1); + { + for (uint32_t i = 0; i < allChannelNames.size(); i++) + { + if (usedChannelNames.size() >= 4) + break; + + if (std::find(usedChannelNames.begin(), usedChannelNames.end(), allChannelNames[i]) == usedChannelNames.end()) + usedChannelNames.push_back(allChannelNames[i]); + } + } - // resize reformattedDataTmp to fit the converted image data - AIGetFormatDetails(inputBufFormat, &numChannelsTmp, &bytesPerChannelTmp, &floatOrIntTmp); - reformattedDataTmp.resize(numChannelsTmp * bytesPerChannelTmp * width * height); + Imf::FrameBuffer frameBuffer; + auto displayWindow = file->header().displayWindow(); - AImgConvertFormat(data, &reformattedDataTmp[0], width, height, inputFormat, inputBufFormat); - inputBuf = &reformattedDataTmp[0]; - } + auto fbMaxW = std::max(dw.max.x, displayWindow.max.x) + 1; + + auto channelType = decodeFormatBytesPerChannel == 4 ? Imf::FLOAT : Imf::HALF; + for (uint32_t i = 0; i < usedChannelNames.size(); i++) + { + auto slice = Imf::Slice(channelType, + destBuffer + i * decodeFormatBytesPerChannel, + usedChannelNames.size() * decodeFormatBytesPerChannel, + fbMaxW * usedChannelNames.size() * decodeFormatBytesPerChannel, + 1, + 1, + 0.0); + + frameBuffer.insert(usedChannelNames[i], slice); + } - int32_t bytesPerChannel, numChannels, floatOrInt; - AIGetFormatDetails(inputBufFormat, &numChannels, &bytesPerChannel, &floatOrInt); + file->setFrameBuffer(frameBuffer); + auto dataWindow = file->header().dataWindow(); + file->readPixels(dataWindow.min.y, dataWindow.max.y); - const char *RGBAChannelNames[] = {"R", "G", "B", "A"}; - const char *GreyScaleChannelName = "Y"; + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + { + int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, width, height, decodeFormat, forceImageFormat); + if (err != AImgErrorCode::AIMG_SUCCESS) + return err; + } - Imf::Header header(width, height); + return AImgErrorCode::AIMG_SUCCESS; + } + catch (const std::exception &e) + { + mErrorDetails = std::string("[AImg::EXRImageLoader::EXRFile::] ") + e.what(); + return AImgErrorCode::AIMG_LOAD_FAILED_INTERNAL; + } + } - for (int32_t i = 0; i < numChannels; i++) + virtual int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) + { + try { - const char *channelName; - if (numChannels == 1) - channelName = GreyScaleChannelName; - else - channelName = RGBAChannelNames[i]; + data = new CallbackIStream(readCallback, tellCallback, seekCallback, callbackData); + file = new Imf::InputFile(*data); + dw = file->header().displayWindow(); + auto header = file->header(); - header.channels().insert(channelName, Imf::Channel((bytesPerChannel == 4) ? Imf::FLOAT : Imf::HALF)); + return AImgErrorCode::AIMG_SUCCESS; + } + catch (const std::exception &e) + { + mErrorDetails = std::string("[AImg::EXRImageLoader::EXRFile::] ") + e.what(); + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; } + } - Imf::FrameBuffer frameBuffer; + int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void *encodingOptions) + { + AIL_UNUSED_PARAM(encodingOptions); - for (int32_t i = 0; i < numChannels; i++) + try { - const char *channelName; - if (numChannels == 1) - channelName = GreyScaleChannelName; - else - channelName = RGBAChannelNames[i]; + std::vector reformattedDataTmp(0); - frameBuffer.insert( - channelName, - Imf::Slice( - (bytesPerChannel == 4) ? Imf::FLOAT : Imf::HALF, - &((char *)inputBuf)[bytesPerChannel * i], - bytesPerChannel * numChannels, - bytesPerChannel * width * numChannels, - 1, 1, - 0.0)); - } + void *inputBuf = data; + AImgFormat inputBufFormat = getWriteFormatExr(inputFormat, outputFormat); - CallbackOStream ostream(writeCallback, tellCallback, seekCallback, callbackData); - Imf::OutputFile file(ostream, header); - file.setFrameBuffer(frameBuffer); - file.writePixels(height); + bool needConversion = inputBufFormat != inputFormat; - return AImgErrorCode::AIMG_SUCCESS; - } - catch (const std::exception &e) - { - mErrorDetails = std::string("[AImg::EXRImageLoader::EXRFile::] ") + e.what(); - return AImgErrorCode::AIMG_WRITE_FAILED_EXTERNAL; - } - } -}; + // need 32F or 16F data, so convert if necessary + if (needConversion) + { + // resize reformattedDataTmp to fit the converted image data + int32_t bytesPerChannelTmp, numChannelsTmp, floatOrIntTmp; + AIGetFormatDetails(inputBufFormat, &numChannelsTmp, &bytesPerChannelTmp, &floatOrIntTmp); + reformattedDataTmp.resize(numChannelsTmp * bytesPerChannelTmp * width * height); -AImgBase *ExrImageLoader::getAImg() -{ - return new ExrFile(); -} + AImgConvertFormat(data, &reformattedDataTmp[0], width, height, inputFormat, inputBufFormat); + inputBuf = &reformattedDataTmp[0]; + } -AImgFormat ExrImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat) -{ - int32_t bytesPerChannel, numChannels, floatOrInt; - AIGetFormatDetails(inputFormat, &numChannels, &bytesPerChannel, &floatOrInt); + int32_t bytesPerChannel, numChannels, floatOrInt; + AIGetFormatDetails(inputBufFormat, &numChannels, &bytesPerChannel, &floatOrInt); - // TODO: allow writing 16-bit float exrs too - /*if(bytesPerChannel < 2) - { - switch(numChannels) + const char *RGBAChannelNames[] = { "R", "G", "B", "A" }; + const char *GreyScaleChannelName = "Y"; + + Imf::Header header(width, height); + + for (int32_t i = 0; i < numChannels; i++) + { + const char *channelName; + if (numChannels == 1) + channelName = GreyScaleChannelName; + else + channelName = RGBAChannelNames[i]; + + header.channels().insert(channelName, Imf::Channel((bytesPerChannel == 4) ? Imf::FLOAT : Imf::HALF)); + } + + Imf::FrameBuffer frameBuffer; + + for (int32_t i = 0; i < numChannels; i++) + { + const char *channelName; + if (numChannels == 1) + channelName = GreyScaleChannelName; + else + channelName = RGBAChannelNames[i]; + + frameBuffer.insert( + channelName, + Imf::Slice( + (bytesPerChannel == 4) ? Imf::FLOAT : Imf::HALF, + &((char *)inputBuf)[bytesPerChannel * i], + bytesPerChannel * numChannels, + bytesPerChannel * width * numChannels, + 1, 1, + 0.0)); + } + + CallbackOStream ostream(writeCallback, tellCallback, seekCallback, callbackData); + Imf::OutputFile file(ostream, header); + file.setFrameBuffer(frameBuffer); + file.writePixels(height); + + return AImgErrorCode::AIMG_SUCCESS; + } + catch (const std::exception &e) { - case 1: return AImgFormat::R16F; - case 2: return AImgFormat::RG16F; - case 3: return AImgFormat::RGB16F; - case 4: return AImgFormat::RGBA16F; + mErrorDetails = std::string("[AImg::EXRImageLoader::EXRFile::] ") + e.what(); + return AImgErrorCode::AIMG_WRITE_FAILED_EXTERNAL; } } - else*/ + }; + + AImgBase *ExrImageLoader::getAImg() { - switch (numChannels) - { - case 1: - return AImgFormat::R32F; - case 2: - return AImgFormat::RG32F; - case 3: - return AImgFormat::RGB32F; - case 4: - return AImgFormat::RGBA32F; - } + return new ExrFile(); } - return AImgFormat::INVALID_FORMAT; -} + bool ExrImageLoader::isFormatSupported(int32_t format) + { + return isFormatSupportedByExr(format); + } + + AImgFormat ExrImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) + { + return getWriteFormatExr(inputFormat, outputFormat); + } } -#endif // HAVE_EXR +#endif // HAVE_EXR \ No newline at end of file diff --git a/src_c/exr.h b/src_c/exr.h index 06c45b5..4bffe37 100644 --- a/src_c/exr.h +++ b/src_c/exr.h @@ -16,7 +16,9 @@ namespace AImg virtual std::string getFileExtension(); virtual int32_t getAImgFileFormatValue(); - virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat); + virtual bool isFormatSupported(int32_t format); + + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat); }; } diff --git a/src_c/hdr.cpp b/src_c/hdr.cpp new file mode 100644 index 0000000..656f78d --- /dev/null +++ b/src_c/hdr.cpp @@ -0,0 +1,184 @@ +#include "hdr.h" +#include "AIL_internal.h" + +#define STBI_ONLY_HDR + +#include "extern/stb_image.h" +#include + +namespace AImg +{ + namespace STBIHDRCallbacks + { + int readCallback(void * user, char * data, int size) + { + CallbackData * callbackFunctions = (CallbackData *)user; + return callbackFunctions->readCallback(callbackFunctions->callbackData, (uint8_t *)data, size); + } + + void seekCallback(void * user, int num_bytes) + { + CallbackData * callbackFunctions = (CallbackData *)user; + callbackFunctions->seekCallback(callbackFunctions->callbackData, num_bytes); + } + + int eofCallback(void * user) + { + CallbackData * callbackFunctions = (CallbackData *)user; + + int startPos = callbackFunctions->tellCallback(callbackFunctions->callbackData); + callbackFunctions->seekCallback(callbackFunctions->callbackData, startPos + 1); + + int newPos = callbackFunctions->tellCallback(callbackFunctions->callbackData); + + callbackFunctions->seekCallback(callbackFunctions->callbackData, startPos); + + return (startPos - newPos) != 0 ? 1 : 0; + } + } + + class HDRFile : public AImgBase + { + public: + + virtual int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) + { + data.readCallback = readCallback; + data.tellCallback = tellCallback; + data.seekCallback = seekCallback; + data.callbackData = callbackData; + + stbi_io_callbacks callback; + callback.read = STBIHDRCallbacks::readCallback; + callback.skip = STBIHDRCallbacks::seekCallback; + callback.eof = STBIHDRCallbacks::eofCallback; + + int startingPosition = tellCallback(callbackData); + stbi_hdr_to_ldr_gamma(1.0f); + stbi_ldr_to_hdr_gamma(1.0f); + + stbi_info_from_callbacks(&callback, &data, &width, &height, &numChannels); + seekCallback(callbackData, startingPosition); + + return AImgErrorCode::AIMG_SUCCESS; + } + + virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) + { + stbi_io_callbacks callbacks; + + callbacks.read = STBIHDRCallbacks::readCallback; + callbacks.skip = STBIHDRCallbacks::seekCallback; + callbacks.eof = STBIHDRCallbacks::eofCallback; + float * loadedData = stbi_loadf_from_callbacks(&callbacks, &data, &width, &height, &numChannels, numChannels); + + if (!loadedData) + { + mErrorDetails = "[AImg::HDRImageLoader::HDRFile::decodeImage] stbi_loadf_from_callbacks failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; + } + + int32_t decodeFormat = AImgFormat::RGB32F; + int32_t numChannels, bytesPerChannel, floatOrInt; + AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + + void* destBuffer = realDestBuffer; + + std::vector convertTmpBuffer(0); + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + { + convertTmpBuffer.resize(width * height * bytesPerChannel * numChannels); + destBuffer = convertTmpBuffer.data(); + } + + memcpy(destBuffer, loadedData, width * height * bytesPerChannel * numChannels); + stbi_image_free(loadedData); + + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + { + int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, width, height, decodeFormat, forceImageFormat); + if (err != AImgErrorCode::AIMG_SUCCESS) + return err; + } + + return AImgErrorCode::AIMG_SUCCESS; + } + + virtual int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) + { + return AImgErrorCode::AIMG_WRITE_NOT_SUPPORTED_FOR_FORMAT; + } + + virtual int32_t getImageInfo(int32_t* width, int32_t* height, int32_t* numChannels, int32_t* bytesPerChannel, int32_t* floatOrInt, int32_t* decodedImgFormat, uint32_t *colourProfileLen) + { + *width = this->width; + *height = this->height; + *numChannels = this->numChannels; + *bytesPerChannel = 4; + *floatOrInt = AImgFloatOrIntType::FITYPE_FLOAT; + *decodedImgFormat = AImgFormat::RGB32F; + + if (colourProfileLen != NULL) + { + *colourProfileLen = 0; + } + return AImgErrorCode::AIMG_SUCCESS; + } + + virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + { + if (colourProfile != NULL) + { + *colourProfileLen = 0; + } + if (profileName != NULL) + { + std::strcpy(profileName, "no_profile"); + } + + return AImgErrorCode::AIMG_SUCCESS; + } + + private: + CallbackData data; + int32_t numChannels, width, height; + }; + + AImgBase * HDRImageLoader::getAImg() + { + return new HDRFile(); + } + + bool HDRImageLoader::canLoadImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData) + { + int startingPosition = tellCallback(callbackData); + + std::vector magic = { 0x23, 0x3f, 0x52, 0x41, 0x44, 0x49, 0x41, 0x4e, 0x43, 0x45, 0x0a }; + + std::vector readBackData; + readBackData.resize(magic.size()); + + readCallback(callbackData, readBackData.data(), (int32_t)magic.size()); + + seekCallback(callbackData, startingPosition); + + return memcmp(magic.data(), readBackData.data(), magic.size()) == 0; + } + + std::string HDRImageLoader::getFileExtension() + { + return "HDR"; + } + + int32_t HDRImageLoader::getAImgFileFormatValue() + { + return HDR_IMAGE_FORMAT; + } + + int32_t HDRImageLoader::initialise() { return AImgErrorCode::AIMG_SUCCESS; } + + bool HDRImageLoader::isFormatSupported(int32_t format) { return format == AImgFormat::RGB32F; } + + AImgFormat HDRImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) { return AImgFormat::RGB32F; } +} \ No newline at end of file diff --git a/src_c/hdr.h b/src_c/hdr.h new file mode 100644 index 0000000..493d84c --- /dev/null +++ b/src_c/hdr.h @@ -0,0 +1,26 @@ +#ifndef ARTOMATIX_HDR_H +#define ARTOMATIX_HDR_H + +#include "ImageLoaderBase.h" + +#include +namespace AImg +{ + class HDRImageLoader : public ImageLoaderBase + { + public: + + virtual AImgBase * getAImg(); + virtual int32_t initialise(); + + virtual bool canLoadImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void* callbackData); + virtual std::string getFileExtension(); + virtual int32_t getAImgFileFormatValue(); + + virtual bool isFormatSupported(int32_t format); + + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat); + }; +} + +#endif \ No newline at end of file diff --git a/src_c/jpeg.cpp b/src_c/jpeg.cpp index ca93ffa..81e5f30 100644 --- a/src_c/jpeg.cpp +++ b/src_c/jpeg.cpp @@ -15,7 +15,6 @@ namespace AImg jpeg_source_mgr pub; void *data; CallbackData callbackFunctionData; - } ArtomatixJPEGSourceMGR; typedef struct @@ -27,8 +26,8 @@ namespace AImg typedef struct { - jpeg_error_mgr pub; - jmp_buf buf; + jpeg_error_mgr pub; + jmp_buf buf; } ArtomatixErrorStruct; namespace JPEGConsts @@ -36,7 +35,6 @@ namespace AImg // Buffer size used in libjpeg const size_t BUFFER_SIZE = 4096; const int Quality = 99; - } namespace JPEGCallbackFunctions @@ -50,21 +48,20 @@ namespace AImg boolean emptyOutputBuffer(j_compress_ptr cinfo) { - ArtomatixJPEGDestinationMGR * dst = (ArtomatixJPEGDestinationMGR *) cinfo->dest; + ArtomatixJPEGDestinationMGR * dst = (ArtomatixJPEGDestinationMGR *)cinfo->dest; dst->callbackFunctionData.writeCallback(dst->callbackFunctionData.callbackData, (uint8_t *)dst->buffer, JPEGConsts::BUFFER_SIZE); - dst->pub.next_output_byte = (JOCTET *) dst->buffer; + dst->pub.next_output_byte = (JOCTET *)dst->buffer; dst->pub.free_in_buffer = JPEGConsts::BUFFER_SIZE; return TRUE; } void termDestination(j_compress_ptr cinfo) { - - ArtomatixJPEGDestinationMGR * dst = (ArtomatixJPEGDestinationMGR *) cinfo->dest; + ArtomatixJPEGDestinationMGR * dst = (ArtomatixJPEGDestinationMGR *)cinfo->dest; size_t datacount = JPEGConsts::BUFFER_SIZE - dst->pub.free_in_buffer; if (datacount > 0) - dst->callbackFunctionData.writeCallback(dst->callbackFunctionData.callbackData, (uint8_t *)dst->buffer, datacount); + dst->callbackFunctionData.writeCallback(dst->callbackFunctionData.callbackData, (uint8_t *)dst->buffer, (int32_t)datacount); } } @@ -82,19 +79,18 @@ namespace AImg { while (num_bytes > (long)src->pub.bytes_in_buffer) { - - num_bytes -= src->pub.bytes_in_buffer; + num_bytes -= (long)(src->pub.bytes_in_buffer); (*src->pub.fill_input_buffer)(cinfo); } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; + src->pub.next_input_byte += (size_t)num_bytes; + src->pub.bytes_in_buffer -= (size_t)num_bytes; } } boolean fillInputBuffer(j_decompress_ptr cinfo) { - ArtomatixJPEGSourceMGR * src = (ArtomatixJPEGSourceMGR *)cinfo->src; + ArtomatixJPEGSourceMGR * src = (ArtomatixJPEGSourceMGR *)cinfo->src; size_t bytesRead = src->callbackFunctionData.readCallback(src->callbackFunctionData.callbackData, (uint8_t *)src->data, JPEGConsts::BUFFER_SIZE); if (bytesRead <= 0) @@ -114,13 +110,13 @@ namespace AImg void lessAnnoyingEmitMessage(j_common_ptr cinfo, int msg_level) { - if (msg_level == 0) + if (msg_level == 0) (*cinfo->err->output_message) (cinfo); } void handleFatalError(j_common_ptr cinfo) { - ArtomatixErrorStruct * err = (ArtomatixErrorStruct *) cinfo->err; + ArtomatixErrorStruct * err = (ArtomatixErrorStruct *)cinfo->err; longjmp(err->buf, 1); } } @@ -130,11 +126,11 @@ namespace AImg if (cinfo->src == NULL) { cinfo->src = (jpeg_source_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(ArtomatixJPEGSourceMGR)); - ((ArtomatixJPEGSourceMGR * )cinfo->src)->data = (void *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, JPEGConsts::BUFFER_SIZE); + ((ArtomatixJPEGSourceMGR *)cinfo->src)->data = (void *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT, JPEGConsts::BUFFER_SIZE); ((ArtomatixJPEGSourceMGR *)cinfo->src)->callbackFunctionData = callbackData; } - ArtomatixJPEGSourceMGR * src = (ArtomatixJPEGSourceMGR *)cinfo->src; + ArtomatixJPEGSourceMGR * src = (ArtomatixJPEGSourceMGR *)cinfo->src; src->pub.init_source = JPEGCallbackFunctions::ReadFunctions::initSource; src->pub.fill_input_buffer = JPEGCallbackFunctions::ReadFunctions::fillInputBuffer; src->pub.skip_input_data = JPEGCallbackFunctions::ReadFunctions::skipInputData; @@ -144,7 +140,6 @@ namespace AImg src->pub.bytes_in_buffer = 0; } - void setArtomatixDestinationMGR(j_compress_ptr cinfo, CallbackData callbackData) { if (cinfo->dest == NULL) @@ -158,9 +153,8 @@ namespace AImg src->pub.init_destination = JPEGCallbackFunctions::WriteFunctions::initDestination; src->pub.empty_output_buffer = JPEGCallbackFunctions::WriteFunctions::emptyOutputBuffer; src->pub.term_destination = JPEGCallbackFunctions::WriteFunctions::termDestination; - src->pub.next_output_byte = (JOCTET *) src->buffer; + src->pub.next_output_byte = (JOCTET *)src->buffer; src->pub.free_in_buffer = JPEGConsts::BUFFER_SIZE; - } int32_t JPEGImageLoader::initialise() @@ -171,10 +165,10 @@ namespace AImg bool JPEGImageLoader::canLoadImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) { AIL_UNUSED_PARAM(tellCallback); - uint8_t magic[] = {0xFF, 0xD8, 0xFF}; + uint8_t magic[] = { 0xFF, 0xD8, 0xFF }; int startingPosition = tellCallback(callbackData); - std::vector header(4); + std::vector header(4); readCallback(callbackData, &header[0], 4); seekCallback(callbackData, startingPosition); @@ -194,223 +188,232 @@ namespace AImg class JPEGFile : public AImgBase { - public: - jpeg_decompress_struct jpeg_read_struct; - ArtomatixErrorStruct err_mgr; + public: + jpeg_decompress_struct jpeg_read_struct; + ArtomatixErrorStruct err_mgr; + JPEGFile() + { + jpeg_create_decompress(&jpeg_read_struct); + } - JPEGFile() - { - jpeg_create_decompress(&jpeg_read_struct); - } + virtual ~JPEGFile() + { + jpeg_destroy_decompress(&jpeg_read_struct); + } - virtual ~JPEGFile() + int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) + { + CallbackData data; + data.callbackData = callbackData; + data.readCallback = readCallback; + data.tellCallback = tellCallback; + data.seekCallback = seekCallback; + + setArtomatixSourceMGR(&jpeg_read_struct, data); + jpeg_read_struct.err = jpeg_std_error(&err_mgr.pub); + + ArtomatixErrorStruct jerr; + jpeg_read_struct.err = jpeg_std_error(&jerr.pub); + //jpeg_read_struct.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; + jpeg_read_struct.err->error_exit = JPEGCallbackFunctions::handleFatalError; + + ArtomatixErrorStruct * err_ptr = (ArtomatixErrorStruct *)jpeg_read_struct.err; + if (setjmp(err_ptr->buf)) { - jpeg_destroy_decompress(&jpeg_read_struct); - } + mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::openImage] jpeg_read_header failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; + } - int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) - { - CallbackData data; - data.callbackData = callbackData; - data.readCallback = readCallback; - data.tellCallback = tellCallback; - data.seekCallback = seekCallback; - - setArtomatixSourceMGR(&jpeg_read_struct, data); - jpeg_read_struct.err = jpeg_std_error(&err_mgr.pub); - - ArtomatixErrorStruct jerr; - jpeg_read_struct.err = jpeg_std_error(&jerr.pub); - //jpeg_read_struct.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; - jpeg_read_struct.err->error_exit = JPEGCallbackFunctions::handleFatalError; - - ArtomatixErrorStruct * err_ptr = (ArtomatixErrorStruct *) jpeg_read_struct.err; - if (setjmp(err_ptr->buf)) - { - mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::openImage] jpeg_read_header failed!"; - - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } + jpeg_read_struct.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; + jpeg_read_struct.err->error_exit = JPEGCallbackFunctions::handleFatalError; + jpeg_read_header(&jpeg_read_struct, TRUE); - jpeg_read_struct.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; - jpeg_read_struct.err->error_exit = JPEGCallbackFunctions::handleFatalError; - jpeg_read_header(&jpeg_read_struct, TRUE); + return AImgErrorCode::AIMG_SUCCESS; + } - return AImgErrorCode::AIMG_SUCCESS; + virtual int32_t getImageInfo(int32_t *width, int32_t *height, int32_t *numChannels, int32_t *bytesPerChannel, int32_t *floatOrInt, int32_t *decodedImgFormat, uint32_t *colourProfileLen) + { + *width = jpeg_read_struct.image_width; + *height = jpeg_read_struct.image_height; + *bytesPerChannel = 1; + *numChannels = jpeg_read_struct.num_components; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + *decodedImgFormat = AImgFormat::_8BITS | AImgFormat::R << (jpeg_read_struct.num_components - 1); + if (colourProfileLen != NULL) + { + *colourProfileLen = 0; } + return AImgErrorCode::AIMG_SUCCESS; + } - virtual int32_t getImageInfo(int32_t *width, int32_t *height, int32_t *numChannels, int32_t *bytesPerChannel, int32_t *floatOrInt, int32_t *decodedImgFormat, uint32_t *colourProfileLen) + virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + { + if (colourProfile != NULL) { - *width = jpeg_read_struct.image_width; - *height = jpeg_read_struct.image_height; - *bytesPerChannel = 1; - *numChannels = jpeg_read_struct.num_components; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - *decodedImgFormat = ((int)AImgFormat::R8U) + jpeg_read_struct.num_components - 1; - if(colourProfileLen != NULL) - { - *colourProfileLen = 0; - } - return AImgErrorCode::AIMG_SUCCESS; + *colourProfileLen = 0; } - - virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + if (profileName != NULL) { - if(colourProfile != NULL) - { - *colourProfileLen = 0; - } - if(profileName != NULL) - { - std::strcpy(profileName, "no_profile"); - } - - return AImgErrorCode::AIMG_SUCCESS; + std::strcpy(profileName, "no_profile"); } - virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) - { - void* destBuffer = realDestBuffer; + return AImgErrorCode::AIMG_SUCCESS; + } - std::vector convertTmpBuffer(0); - if(forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != AImgFormat::RGB8U) - { - int32_t numChannels, bytesPerChannel, floatOrInt; - AIGetFormatDetails(AImgFormat::RGB8U, &numChannels, &bytesPerChannel, &floatOrInt); + virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) + { + void* destBuffer = realDestBuffer; - convertTmpBuffer.resize(jpeg_read_struct.image_width * jpeg_read_struct.image_height * bytesPerChannel * numChannels); - destBuffer = &convertTmpBuffer[0]; - } + std::vector convertTmpBuffer(0); + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != AImgFormat::RGB8U) + { + int32_t numChannels, bytesPerChannel, floatOrInt; + AIGetFormatDetails(AImgFormat::RGB8U, &numChannels, &bytesPerChannel, &floatOrInt); - ArtomatixErrorStruct jerr; - jpeg_read_struct.err = jpeg_std_error(&jerr.pub); - jpeg_read_struct.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; - jpeg_read_struct.err->error_exit = JPEGCallbackFunctions::handleFatalError; + convertTmpBuffer.resize(jpeg_read_struct.image_width * jpeg_read_struct.image_height * bytesPerChannel * numChannels); + destBuffer = &convertTmpBuffer[0]; + } - ArtomatixErrorStruct * err_ptr = (ArtomatixErrorStruct *) jpeg_read_struct.err; + ArtomatixErrorStruct jerr; + jpeg_read_struct.err = jpeg_std_error(&jerr.pub); + jpeg_read_struct.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; + jpeg_read_struct.err->error_exit = JPEGCallbackFunctions::handleFatalError; - if (setjmp(err_ptr->buf)) - { - mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::decodeImage] jpeg_start_decompress failed!"; - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } + ArtomatixErrorStruct * err_ptr = (ArtomatixErrorStruct *)jpeg_read_struct.err; - jpeg_start_decompress(&jpeg_read_struct); + if (setjmp(err_ptr->buf)) + { + mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::decodeImage] jpeg_start_decompress failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; + } - int row_stride = jpeg_read_struct.output_components * jpeg_read_struct.output_width; + jpeg_start_decompress(&jpeg_read_struct); - JSAMPROW buffer[1]; + int row_stride = jpeg_read_struct.output_components * jpeg_read_struct.output_width; - buffer[0] = (JSAMPROW) destBuffer; + JSAMPROW buffer[1]; - if (setjmp(err_ptr->buf)) - { - mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::decodeImage] jpeg_read_scanlines failed!"; - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } + buffer[0] = (JSAMPROW)destBuffer; - while (jpeg_read_struct.output_scanline < jpeg_read_struct.output_height) - { - jpeg_read_scanlines(&jpeg_read_struct, buffer, 1); - buffer[0] = (uint8_t * )buffer[0] + row_stride; - } + if (setjmp(err_ptr->buf)) + { + mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::decodeImage] jpeg_read_scanlines failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; + } - jpeg_finish_decompress(&jpeg_read_struct); + while (jpeg_read_struct.output_scanline < jpeg_read_struct.output_height) + { + jpeg_read_scanlines(&jpeg_read_struct, buffer, 1); + buffer[0] = (uint8_t *)buffer[0] + row_stride; + } - if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != AImgFormat::RGB8U) - { - int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, jpeg_read_struct.image_width, jpeg_read_struct.image_height, AImgFormat::RGB8U, forceImageFormat); - if(err != AImgErrorCode::AIMG_SUCCESS) - return err; - } + jpeg_finish_decompress(&jpeg_read_struct); - return AImgErrorCode::AIMG_SUCCESS; + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != AImgFormat::RGB8U) + { + int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, jpeg_read_struct.image_width, jpeg_read_struct.image_height, AImgFormat::RGB8U, forceImageFormat); + if (err != AImgErrorCode::AIMG_SUCCESS) + return err; } - int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, - WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) - { - AIL_UNUSED_PARAM(encodingOptions); + return AImgErrorCode::AIMG_SUCCESS; + } - std::vector convertBuffer(0); - if (inputFormat != AImgFormat::RGB8U) - { - convertBuffer.resize(width * height * 3); + int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) + { + AIL_UNUSED_PARAM(encodingOptions); + AIL_UNUSED_PARAM(outputFormat); - int32_t convertError = AImgConvertFormat(data, &convertBuffer[0], width, height, inputFormat, AImgFormat::RGB8U); + std::vector convertBuffer(0); + if (inputFormat != AImgFormat::RGB8U) + { + convertBuffer.resize(width * height * 3); - if (convertError != AImgErrorCode::AIMG_SUCCESS) - return convertError; - data = &convertBuffer[0]; - } + int32_t convertError = AImgConvertFormat(data, &convertBuffer[0], width, height, inputFormat, AImgFormat::RGB8U); - CallbackData dataStruct; - dataStruct.writeCallback = writeCallback; - dataStruct.tellCallback = tellCallback; - dataStruct.seekCallback = seekCallback; - dataStruct.callbackData = callbackData; + if (convertError != AImgErrorCode::AIMG_SUCCESS) + return convertError; + data = &convertBuffer[0]; + } - ArtomatixErrorStruct jerr; - jpeg_compress_struct cinfo; - cinfo.err = jpeg_std_error(&jerr.pub); - cinfo.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; - cinfo.err->error_exit = JPEGCallbackFunctions::handleFatalError; - jpeg_create_compress(&cinfo); + CallbackData dataStruct; + dataStruct.writeCallback = writeCallback; + dataStruct.tellCallback = tellCallback; + dataStruct.seekCallback = seekCallback; + dataStruct.callbackData = callbackData; - setArtomatixDestinationMGR(&cinfo, dataStruct); + ArtomatixErrorStruct jerr; + jpeg_compress_struct cinfo; + cinfo.err = jpeg_std_error(&jerr.pub); + cinfo.err->emit_message = JPEGCallbackFunctions::lessAnnoyingEmitMessage; + cinfo.err->error_exit = JPEGCallbackFunctions::handleFatalError; + jpeg_create_compress(&cinfo); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; + setArtomatixDestinationMGR(&cinfo, dataStruct); - jpeg_set_defaults(&cinfo); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; - jpeg_set_quality(&cinfo, JPEGConsts::Quality, TRUE); + jpeg_set_defaults(&cinfo); - if (setjmp(jerr.buf)) - { - mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::writeImage] jpeg_start_compress failed!"; - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } - jpeg_start_compress(&cinfo, TRUE); + jpeg_set_quality(&cinfo, JPEGConsts::Quality, TRUE); - int row_stride = width * cinfo.input_components; + if (setjmp(jerr.buf)) + { + mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::writeImage] jpeg_start_compress failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; + } + jpeg_start_compress(&cinfo, TRUE); - JSAMPROW row_pointer[1]; + int row_stride = width * cinfo.input_components; + JSAMPROW row_pointer[1]; - if (setjmp(jerr.buf)) - { - mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::writeImage] jpeg_write_scanlines failed!"; - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } + if (setjmp(jerr.buf)) + { + mErrorDetails = "[AImg::JPEGImageLoader::JPEGFile::writeImage] jpeg_write_scanlines failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; + } - while(cinfo.next_scanline < cinfo.image_height) - { - row_pointer[0] = (uint8_t *)data + row_stride * cinfo.next_scanline; - jpeg_write_scanlines(&cinfo, row_pointer, 1); - } + while (cinfo.next_scanline < cinfo.image_height) + { + row_pointer[0] = (uint8_t *)data + row_stride * cinfo.next_scanline; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); - return AImgErrorCode::AIMG_SUCCESS; - } + return AImgErrorCode::AIMG_SUCCESS; + } }; - AImgFormat JPEGImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat) + AImgFormat JPEGImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) { AIL_UNUSED_PARAM(inputFormat); + AIL_UNUSED_PARAM(outputFormat); return AImgFormat::RGB8U; } + bool JPEGImageLoader::isFormatSupported(int32_t format) + { + bool floatFormat = format & AImgFormat::FLOAT_FORMAT; + bool bitDepth8 = format & AImgFormat::_8BITS; + bool rgb = format & AImgFormat::RGB; + + bool isSupported = !floatFormat && bitDepth8 && rgb; + return isSupported; + } + AImgBase* JPEGImageLoader::getAImg() { return new JPEGFile(); } } -#endif // HAVE_JPEG +#endif // HAVE_JPEG \ No newline at end of file diff --git a/src_c/jpeg.h b/src_c/jpeg.h index b57e11b..207e94f 100644 --- a/src_c/jpeg.h +++ b/src_c/jpeg.h @@ -16,7 +16,9 @@ namespace AImg virtual std::string getFileExtension(); virtual int32_t getAImgFileFormatValue(); - virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat); + virtual bool isFormatSupported(int32_t format); + + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat); }; } diff --git a/src_c/png.cpp b/src_c/png.cpp index af24c40..a18d4b3 100644 --- a/src_c/png.cpp +++ b/src_c/png.cpp @@ -11,7 +11,6 @@ namespace AImg { - int32_t PNGImageLoader::initialise() { return AImgErrorCode::AIMG_SUCCESS; @@ -25,7 +24,7 @@ namespace AImg seekCallback(callbackData, startingPosition); - uint8_t png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + uint8_t png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; return ((int32_t)(memcmp(&header[0], &png_signature[0], 8))) == 0; } @@ -34,14 +33,14 @@ namespace AImg { CallbackData callbackData = *((CallbackData *)png_get_io_ptr(png_ptr)); - callbackData.readCallback(callbackData.callbackData, data, length); + callbackData.readCallback(callbackData.callbackData, data, (int32_t)length); } void png_custom_write_data(png_struct* png_ptr, png_byte* data, png_size_t length) { CallbackData callbackData = *((CallbackData *)png_get_io_ptr(png_ptr)); - callbackData.writeCallback(callbackData.callbackData, data, length); + callbackData.writeCallback(callbackData.callbackData, data, (int32_t)length); } std::string PNGImageLoader::getFileExtension() @@ -59,25 +58,50 @@ namespace AImg AIL_UNUSED_PARAM(png_ptr); } + bool isFormatSupportedByPng(int32_t format) + { + bool isNotFloatFormat = !(format & AImgFormat::FLOAT_FORMAT); + bool is8Or16Bit = (format & AImgFormat::_8BITS || format & AImgFormat::_16BITS); + bool isNotRG8U = format != AImgFormat::RG8U; + bool isNotRG16U = format != AImgFormat::RG16U; + bool isSupported = isNotFloatFormat + && is8Or16Bit + && isNotRG8U + && isNotRG16U; + + return isSupported; + } - AImgFormat getWhatFormatWillBeWrittenForDataPNG(int32_t inputFormat) + AImgFormat getWhatFormatWillBeWrittenForDataPNG(int32_t inputFormat, int32_t outputFormat) { + if (isFormatSupportedByPng(outputFormat)) + { + return (AImgFormat)outputFormat; + } + int32_t numChannels, bytesPerChannel, floatOrInt; AIGetFormatDetails(inputFormat, &numChannels, &bytesPerChannel, &floatOrInt); - if(floatOrInt == AImgFloatOrIntType::FITYPE_FLOAT) - return (AImgFormat) (AImgFormat::R16U + numChannels - 1); // convert to 16U version with same channelNum + if (floatOrInt == AImgFloatOrIntType::FITYPE_FLOAT) + { + AImgFormat outFormat = (AImgFormat)(AImgFormat::_16BITS | (AImgFormat::R << (numChannels - 1))); // convert to 16U version with same channelNum - if (inputFormat == AImgFormat::RG8U) - return AImgFormat::RGB16U; + if (outFormat == AImgFormat::RG16U) + return AImgFormat::RGB16U; + + return outFormat; + } if (inputFormat == AImgFormat::RG8U) + return AImgFormat::RGB8U; + + if (inputFormat == AImgFormat::RG16U) return AImgFormat::RGB16U; - if(inputFormat >= AImgFormat::R8U && inputFormat <= AImgFormat::RGBA8U) + if(inputFormat & AImgFormat::_8BITS) return (AImgFormat)inputFormat; - if(inputFormat >= AImgFormat::R16U && inputFormat <= AImgFormat::RGBA16U) + if(inputFormat & AImgFormat::_16BITS) return (AImgFormat)inputFormat; return AImgFormat::INVALID_FORMAT; @@ -150,24 +174,38 @@ namespace AImg png_set_expand(png_read_ptr); bit_depth = 8; } - if (png_get_valid(png_read_ptr, png_info_ptr, PNG_INFO_tRNS)) + + bool hasTransparency = png_get_valid(png_read_ptr, png_info_ptr, PNG_INFO_tRNS); + bool isGrayWithAlpha = colour_type == PNG_COLOR_TYPE_GRAY_ALPHA; + + bool numChannelsChanged = false; + + if (hasTransparency) { png_set_expand(png_read_ptr); - if (colour_type == PNG_COLOR_TYPE_GRAY || colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) - { - // expand grayscale images with transparency to 4-channel RGBA because if we don't then we end up - // with gray in R and alpha in G, and converting that up to RGBA will not yield the correct results - png_set_gray_to_rgb(png_read_ptr); - numChannels = 3; - } - numChannels++; } - if(!(png_get_iCCP(png_read_ptr, png_info_ptr, &profileName, &compressionMethod, &compressedProfile, &compressedProfileLen) & PNG_INFO_iCCP)) + + if ((hasTransparency && colour_type == PNG_COLOR_TYPE_GRAY) || isGrayWithAlpha) { - compressedProfile = NULL; - profileName = NULL; + // expand grayscale images with transparency to 4-channel RGBA because if we don't then we end up + // with gray in R and alpha in G, and converting that up to RGBA will not yield the correct results + png_set_gray_to_rgb(png_read_ptr); + + numChannels = 4; + numChannelsChanged = true; + } + + if (!numChannelsChanged) + { + // Don't load color profile if the number of color channels changes. It would be invalid + + if (!(png_get_iCCP(png_read_ptr, png_info_ptr, &profileName, &compressionMethod, &compressedProfile, &compressedProfileLen) & PNG_INFO_iCCP)) + { + compressedProfile = NULL; + profileName = NULL; + } } return AImgErrorCode::AIMG_SUCCESS; @@ -295,7 +333,9 @@ namespace AImg return AImgErrorCode::AIMG_SUCCESS; } - int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) + int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, + const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) { AIL_UNUSED_PARAM(tellCallback); AIL_UNUSED_PARAM(seekCallback); @@ -319,7 +359,7 @@ namespace AImg png_set_write_fn(png_write_ptr, (void *)callbackDataStruct, png_custom_write_data, flush_data_noop_func); - int32_t writeFormat = getWhatFormatWillBeWrittenForDataPNG(inputFormat); + int32_t writeFormat = getWhatFormatWillBeWrittenForDataPNG(inputFormat, outputFormat); std::vector convertBuffer(0); @@ -334,6 +374,17 @@ namespace AImg if (convertError != AImgErrorCode::AIMG_SUCCESS) return convertError; data = &convertBuffer[0]; + + int outChannels = numChannels; + AIGetFormatDetails(inputFormat, &numChannels, &bytesPerChannel, &floatOrInt); + + if (numChannels != outChannels + && (numChannels != 4 || outChannels != 3) + && (numChannels != 3 || outChannels != 4)) + { + // Don't save color profile if the number of color channels changed + colourProfile = NULL; + } } png_byte colour_type; @@ -357,7 +408,6 @@ namespace AImg colour_type = PNG_COLOR_TYPE_RGB_ALPHA; bit_depth = 8; break; - case R16U: colour_type = PNG_COLOR_TYPE_GRAY; bit_depth = 16; @@ -385,10 +435,15 @@ namespace AImg int32_t numChannels, bytesPerChannel, floatOrInt; AIGetFormatDetails(writeFormat, &numChannels, &bytesPerChannel, &floatOrInt); - void ** ptrs = (void **)malloc(sizeof(size_t) * height); + size_t step = width * numChannels * bytesPerChannel; - for (int32_t y=0; y < height; y++) - ptrs[y] = (void *)((size_t)data + y*width * numChannels * bytesPerChannel); + png_bytepp ptrs = (png_bytepp)malloc(sizeof(png_bytep) * height); + + ptrs[0] = (png_bytep)data; + for (int32_t y = 1; y < height; y++) + { + ptrs[y] = ptrs[y - 1] + step; + } png_set_IHDR(png_write_ptr, png_info_ptr, width, height, bit_depth, colour_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); @@ -455,9 +510,14 @@ namespace AImg }; - AImgFormat PNGImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat) + AImgFormat PNGImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) + { + return getWhatFormatWillBeWrittenForDataPNG(inputFormat, outputFormat); + } + + bool PNGImageLoader::isFormatSupported(int32_t format) { - return getWhatFormatWillBeWrittenForDataPNG(inputFormat); + return isFormatSupportedByPng(format); } AImgBase* PNGImageLoader::getAImg() diff --git a/src_c/png.h b/src_c/png.h index da4ee6f..fb1c853 100644 --- a/src_c/png.h +++ b/src_c/png.h @@ -16,7 +16,9 @@ namespace AImg virtual std::string getFileExtension(); virtual int32_t getAImgFileFormatValue(); - virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat); + virtual bool isFormatSupported(int32_t format); + + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat); }; } diff --git a/src_c/tests/CMakeLists.txt b/src_c/tests/CMakeLists.txt index 03f0f97..a66b0d8 100644 --- a/src_c/tests/CMakeLists.txt +++ b/src_c/tests/CMakeLists.txt @@ -17,14 +17,10 @@ if(AIMG_TESTS_ENABLED) set(GTEST_MAINLIB "optimized;${GTEST_LOCATION}/Release/${LIBPREFIX}gtest_main${LIBSUFFIX};debug;${GTEST_LOCATION}/Debug/${LIBPREFIX}gtest_main${LIBSUFFIX}") set(GTEST_CMAKE_ARGS "-Dgtest_force_shared_crt=ON") endif() + + hunter_add_package(GTest) + find_package(GTest CONFIG REQUIRED) - include(ExternalProject) - ExternalProject_Add(gtest - URL https://github.com/google/googletest/archive/release-1.7.0.zip - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest - CMAKE_ARGS "${GTEST_CMAKE_ARGS}" - INSTALL_COMMAND "" - ) find_package(Threads) @@ -34,9 +30,9 @@ if(AIMG_TESTS_ENABLED) add_executable("test_${test_name}" "${test_name}.cpp" "testCommon.cpp" "testCommon.h") add_dependencies("test_${test_name}" gtest) - target_include_directories("test_${test_name}" PRIVATE ${GTEST_INCLUDES}) + target_include_directories("test_${test_name}" PRIVATE GTest::main) target_compile_definitions("test_${test_name}" PRIVATE TEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}") - target_link_libraries("test_${test_name}" ${GTEST_LIBRARY} ${GTEST_MAINLIB} ${link_libs} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries("test_${test_name}" GTest::main ${link_libs} ${CMAKE_THREAD_LIBS_INIT}) set_target_properties("test_${test_name}" PROPERTIES COMPILE_FLAGS "${AIL_COMPILE_FLAGS}") @@ -67,6 +63,10 @@ if(AIMG_TESTS_ENABLED) ail_add_test(tiff "AIL" Yes) endif() + if(HDR_ENABLED) + ail_add_test(hdr "AIL" Yes) + endif() + add_custom_target(aitest ${all_tests}) set_target_properties(aitest PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) endif() diff --git a/src_c/tests/exr.cpp b/src_c/tests/exr.cpp index c4d555b..c592d66 100644 --- a/src_c/tests/exr.cpp +++ b/src_c/tests/exr.cpp @@ -7,6 +7,64 @@ #include +void WriteImageTest(AImgFormat decodeFormat, AImgFormat writeFormat, AImgFormat expectedWritten = AImgFormat::INVALID_FORMAT) +{ + if (expectedWritten < 0) + { + expectedWritten = writeFormat; + } + + auto data = readFile(getImagesDir() + "/exr/grad_32.exr"); + + ReadCallback readCallback = NULL; + WriteCallback writeCallback = NULL; + TellCallback tellCallback = NULL; + SeekCallback seekCallback = NULL; + void* callbackData = NULL; + + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); + + AImgHandle img = NULL; + AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); + + int32_t width = 64; + int32_t height = 32; + + int32_t numChannels, bytesPerChannel, floatOrInt; + AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + + int32_t fileSize = width*height*numChannels*bytesPerChannel; + + std::vector imgData(fileSize, 0); + + AImgDecodeImage(img, &imgData[0], decodeFormat); + AImgClose(img); + AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); + + std::vector fileData(fileSize); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], (int32_t)fileData.size()); + + AImgHandle wImg = AImgGetAImg(AImgFileFormat::EXR_IMAGE_FORMAT); + AImgWriteImage(wImg, &imgData[0], width, height, decodeFormat, writeFormat, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + AImgClose(wImg); + + seekCallback(callbackData, 0); + + AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); + + int32_t _, writtenFormat; + AImgGetInfo(img, &_, &_, &_, &_, &_, &writtenFormat, NULL); + ASSERT_EQ(writtenFormat, expectedWritten); + + std::vector imgData2(fileSize, 0); + AImgDecodeImage(img, &imgData2[0], decodeFormat); + AImgClose(img); + AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); + + for (uint32_t i = 0; i < imgData2.size(); i++) + ASSERT_LE(abs(imgData[i] - imgData2[i]), 1); +} + TEST(Exr, TestDetectExr) { ASSERT_TRUE(detectImage("/exr/grad_32.exr", EXR_IMAGE_FORMAT)); @@ -33,7 +91,7 @@ TEST(Exr, TestMemoryCallbacksRead) { std::vector data(10); for(size_t i = 0; i < data.size(); i++) - data[i] = i; + data[i] = (uint8_t)i; ReadCallback readCallback = NULL; WriteCallback writeCallback = NULL; @@ -41,7 +99,7 @@ TEST(Exr, TestMemoryCallbacksRead) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); std::vector readBuf(data.size()); std::fill(readBuf.begin(), readBuf.end(), 100); @@ -87,7 +145,7 @@ TEST(Exr, TestReadExr) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); AImgHandle img = NULL; AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); @@ -123,7 +181,7 @@ TEST(Exr, TestWriteExr) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); AImgHandle img = NULL; AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); @@ -138,10 +196,10 @@ TEST(Exr, TestWriteExr) AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); std::vector fileData(4096); // fixed size buffer for a file write, not the best but it'll do for this test - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], (int32_t)fileData.size()); AImgHandle wImg = AImgGetAImg(AImgFileFormat::EXR_IMAGE_FORMAT); - AImgWriteImage(wImg, &imgData[0], width, height, AImgFormat::RGB32F, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + AImgWriteImage(wImg, &imgData[0], width, height, AImgFormat::RGB32F, AImgFormat::INVALID_FORMAT, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); AImgClose(wImg); seekCallback(callbackData, 0); @@ -209,7 +267,7 @@ TEST(Exr, TestOpenBadImage) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); AImgHandle img = NULL; int32_t err = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); @@ -237,51 +295,32 @@ TEST(Exr, TestOpenEmptyStream) TEST(Exr, TestWriteExr16) { - auto data = readFile(getImagesDir() + "/exr/grad_32.exr"); - - ReadCallback readCallback = NULL; - WriteCallback writeCallback = NULL; - TellCallback tellCallback = NULL; - SeekCallback seekCallback = NULL; - void* callbackData = NULL; - - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); - - AImgHandle img = NULL; - AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); - - int32_t width = 64; - int32_t height = 32; - - std::vector imgData(width*height*3, 0.0f); - - AImgDecodeImage(img, &imgData[0], AImgFormat::RGB16F); - AImgClose(img); - AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); - - std::vector fileData(8192); // fixed size buffer for a file write, not the best but it'll do for this test - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); - - AImgHandle wImg = AImgGetAImg(AImgFileFormat::EXR_IMAGE_FORMAT); - AImgWriteImage(wImg, &imgData[0], width, height, AImgFormat::RGB16F, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); - AImgClose(wImg); - - seekCallback(callbackData, 0); - - - AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); + WriteImageTest(AImgFormat::RGB16F, AImgFormat::RGB16F); +} - int32_t _, writtenFormat; - AImgGetInfo(img, &_, &_, &_, &_, &_, &writtenFormat, NULL); - ASSERT_EQ(writtenFormat, AImgFormat::RGB16F); +TEST(Exr, TestWriteConvert32To16bits) +{ + WriteImageTest(AImgFormat::RGB32F, AImgFormat::RGB16F); +} +TEST(Exr, TestWriteConvert16To32bits) +{ + WriteImageTest(AImgFormat::RGB16F, AImgFormat::RGB32F); +} - std::vector imgData2(width*height*3, 0.0f); - AImgDecodeImage(img, &imgData2[0], AImgFormat::INVALID_FORMAT); - AImgClose(img); - AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); +TEST(Exr, TestWrite8bits) +{ + WriteImageTest(AImgFormat::RGB8U, AImgFormat::INVALID_FORMAT, AImgFormat::RGB16F); +} +TEST(Exr, TestWrite16bitsU) +{ + WriteImageTest(AImgFormat::R16U, AImgFormat::R16U, AImgFormat::R16F); +} - for(uint32_t i = 0; i < imgData2.size(); i++) - ASSERT_EQ(imgData[i], imgData2[i]); +TEST(Exr, TestSupportedFormat) +{ + ASSERT_FALSE(AImgIsFormatSupported(AImgFileFormat::EXR_IMAGE_FORMAT, AImgFormat::_8BITS)); + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::EXR_IMAGE_FORMAT, AImgFormat::_16BITS | AImgFormat::FLOAT_FORMAT)); + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::EXR_IMAGE_FORMAT, AImgFormat::_32BITS | AImgFormat::FLOAT_FORMAT)); } #endif diff --git a/src_c/tests/hdr.cpp b/src_c/tests/hdr.cpp new file mode 100644 index 0000000..1bae989 --- /dev/null +++ b/src_c/tests/hdr.cpp @@ -0,0 +1,98 @@ +#include "testCommon.h" +#include +#include "../AIL.h" +#define STBI_ONLY_HDR +#define STB_IMAGE_IMPLEMENTATION +#include "../extern/stb_image.h" + +std::vector decodeHDRFile(const std::string & path) +{ + int width, height, comp; + FILE * file = fopen(path.c_str(), "rb"); + + stbi_hdr_to_ldr_gamma(1.0f); + stbi_ldr_to_hdr_gamma(1.0f); + + uint8_t * loadedData = stbi_load_from_file(file, &width, &height, &comp, 0); + std::vector data(width * height * comp); + + memcpy(&data[0], loadedData, width*height*comp); + stbi_image_free(loadedData); + fclose(file); + + return data; +} + +TEST(HDR, TestDetectHDR) +{ + ASSERT_TRUE(detectImage("/hdr/test-env.hdr", HDR_IMAGE_FORMAT)); +} + +TEST(HDR, TestDetectBadHDR) +{ + ASSERT_FALSE(detectImage("/jpeg/test.jpeg", HDR_IMAGE_FORMAT)); +} + +TEST(HDR, TestReadHDRFile) +{ + auto data = readFile(getImagesDir() + "/hdr/test-env.hdr"); + + ReadCallback readCallback = NULL; + WriteCallback writeCallback = NULL; + TellCallback tellCallback = NULL; + SeekCallback seekCallback = NULL; + void* callbackData = NULL; + + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); + + AImgHandle img = NULL; + AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); + + int32_t width; + int32_t height; + int32_t numChannels; + int32_t bytesPerChannel; + int32_t floatOrInt; + int32_t imgFmt; + + AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &imgFmt, NULL); + + std::vector imgData(width*height*numChannels*bytesPerChannel, 78); + + int32_t error = AImgDecodeImage(img, &imgData[0], AImgFormat::INVALID_FORMAT); + + if (error != AImgErrorCode::AIMG_SUCCESS) + { + std::cout << AImgGetErrorDetails(img) << std::endl; + } + + auto knownData = decodeHDRFile(getImagesDir() + "/hdr/test-env.hdr"); + + auto ptr = knownData.data(); + for (int32_t y = 0; y < height; y++) + { + for (int32_t x = 0; x < width; x++) + { + if (knownData[x + width*y] != imgData[(x + width*y)]) + { + break; + } + ASSERT_EQ(knownData[x + width*y], imgData[(x + width*y)]); + } + } + + AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); + AImgClose(img); +} + +int main(int argc, char * argv[]) +{ + AImgInitialise(); + + ::testing::InitGoogleTest(&argc, argv); + int retval = RUN_ALL_TESTS(); + + AImgCleanUp(); + + return retval; +} \ No newline at end of file diff --git a/src_c/tests/jpeg.cpp b/src_c/tests/jpeg.cpp index 47f8118..5e171e1 100644 --- a/src_c/tests/jpeg.cpp +++ b/src_c/tests/jpeg.cpp @@ -25,9 +25,9 @@ std::vector decodeJPEGFile(const std::string & path) std::vector Vbuffer(row_stride*cinfo.output_height); - uint8_t * rowBuffer = &Vbuffer[0]; + uint8_t * rowBuffer = &Vbuffer[0]; JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW)); - buffer[0] = (JSAMPROW) malloc(row_stride * sizeof(JSAMPLE)); + buffer[0] = (JSAMPROW)malloc(row_stride * sizeof(JSAMPLE)); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); @@ -64,7 +64,6 @@ bool testReadJpegFile(const std::string& path) return false; } - int32_t width; int32_t height; int32_t numChannels; @@ -86,11 +85,11 @@ bool testReadJpegFile(const std::string& path) auto knownData = decodeJPEGFile(getImagesDir() + path); - for(int32_t y = 0; y < height; y++) + for (int32_t y = 0; y < height; y++) { - for(int32_t x = 0; x < width; x++) + for (int32_t x = 0; x < width; x++) { - if(knownData[x + width*y] != imgData[(x + width*y)]) + if (knownData[x + width*y] != imgData[(x + width*y)]) return false; } } @@ -101,45 +100,7 @@ bool testReadJpegFile(const std::string& path) return true; } - -TEST(JPEG, TestDetectJPEG) -{ - ASSERT_TRUE(detectImage("/jpeg/test.jpeg", JPEG_IMAGE_FORMAT)); -} - -TEST(JPEG, TestReadJPEGAttrs) -{ - ASSERT_TRUE(validateImageHeaders("/jpeg/test.jpeg", 640, 400, 3, 1, AImgFloatOrIntType::FITYPE_INT, AImgFormat::RGB8U)); -} - -TEST(JPEG, TestReadJPEGAttrsGreyscale) -{ - ASSERT_TRUE(validateImageHeaders("/jpeg/greyscale.jpeg", 2048, 2048, 1, 1, AImgFloatOrIntType::FITYPE_INT, AImgFormat::R8U)); -} - - -TEST(JPEG, TestCompareForceImageFormat1) -{ - ASSERT_TRUE(compareForceImageFormat("/jpeg/test.jpeg")); -} - -TEST(JPEG, TestReadJPEGFile1) -{ - ASSERT_TRUE(testReadJpegFile("/jpeg/test.jpeg")); -} - -TEST(JPEG, TestReadJPEGFile2) -{ - ASSERT_TRUE(testReadJpegFile("/jpeg/karl.jpeg")); -} - -TEST(JPEG, TestReadJPEGFile3) -{ - ASSERT_TRUE(testReadJpegFile("/jpeg/karl_comment.jpeg")); -} - - -TEST(JPEG, TestWriteJPEG) +void TestWriteJpeg(AImgFormat decodeFormat, AImgFormat writeFormat, AImgFormat expectedWritten = AImgFormat::INVALID_FORMAT) { auto data = readFile(getImagesDir() + "/jpeg/test.jpeg"); @@ -163,19 +124,34 @@ TEST(JPEG, TestWriteJPEG) AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &fmt, NULL); + if (decodeFormat < 0) + { + decodeFormat = (AImgFormat)fmt; + } + if (writeFormat < 0) + { + writeFormat = decodeFormat; + } + if (expectedWritten < 0) + { + expectedWritten = writeFormat; + } + + AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + std::vector imgData(width*height*numChannels * bytesPerChannel, 78); - AImgDecodeImage(img, &imgData[0], AImgFormat::INVALID_FORMAT); + AImgDecodeImage(img, &imgData[0], decodeFormat); AImgClose(img); AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); - std::vector fileData(width * height * numChannels * bytesPerChannel * 5); + std::vector fileData(width * height * numChannels * bytesPerChannel * 5); AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); AImgHandle wImg = AImgGetAImg(AImgFileFormat::JPEG_IMAGE_FORMAT); - AImgWriteImage(wImg, &imgData[0], width, height, fmt, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + AImgWriteImage(wImg, &imgData[0], width, height, decodeFormat, writeFormat, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); AImgClose(wImg); seekCallback(callbackData, 0); @@ -183,11 +159,16 @@ TEST(JPEG, TestWriteJPEG) AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); std::vector imgData2(width*height*numChannels*bytesPerChannel, 0); - AImgDecodeImage(img, &imgData2[0], AImgFormat::INVALID_FORMAT); + AImgDecodeImage(img, &imgData2[0], decodeFormat); + + int32_t _, writtenFormat; + AImgGetInfo(img, &_, &_, &_, &_, &_, &writtenFormat, NULL); + ASSERT_EQ(writtenFormat, expectedWritten); + AImgClose(img); AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); - for(uint32_t i = 0; i < imgData2.size(); i++) + for (uint32_t i = 0; i < imgData2.size(); i++) { uint8_t diff = abs(imgData[i] - imgData2[i]); @@ -196,6 +177,60 @@ TEST(JPEG, TestWriteJPEG) } } +TEST(JPEG, TestDetectJPEG) +{ + ASSERT_TRUE(detectImage("/jpeg/test.jpeg", JPEG_IMAGE_FORMAT)); +} + +TEST(JPEG, TestReadJPEGAttrs) +{ + ASSERT_TRUE(validateImageHeaders("/jpeg/test.jpeg", 640, 400, 3, 1, AImgFloatOrIntType::FITYPE_INT, AImgFormat::RGB8U)); +} + +TEST(JPEG, TestReadJPEGAttrsGreyscale) +{ + ASSERT_TRUE(validateImageHeaders("/jpeg/greyscale.jpeg", 2048, 2048, 1, 1, AImgFloatOrIntType::FITYPE_INT, AImgFormat::R8U)); +} + +TEST(JPEG, TestCompareForceImageFormat1) +{ + ASSERT_TRUE(compareForceImageFormat("/jpeg/test.jpeg")); +} + +TEST(JPEG, TestReadJPEGFile1) +{ + ASSERT_TRUE(testReadJpegFile("/jpeg/test.jpeg")); +} + +TEST(JPEG, TestReadJPEGFile2) +{ + ASSERT_TRUE(testReadJpegFile("/jpeg/karl.jpeg")); +} + +TEST(JPEG, TestReadJPEGFile3) +{ + ASSERT_TRUE(testReadJpegFile("/jpeg/karl_comment.jpeg")); +} + +TEST(JPEG, TestWriteJPEG) +{ + TestWriteJpeg(AImgFormat::INVALID_FORMAT, AImgFormat::INVALID_FORMAT); +} + +TEST(JPEG, TestWriteConvert) +{ + TestWriteJpeg(AImgFormat::RGB16U, AImgFormat::INVALID_FORMAT, AImgFormat::RGB8U); + + TestWriteJpeg(AImgFormat::RGB16U, AImgFormat::RGB8U); +} + +TEST(JPEG, TestSupportedFormat) +{ + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::JPEG_IMAGE_FORMAT, AImgFormat::_8BITS | AImgFormat::RGB)); + ASSERT_FALSE(AImgIsFormatSupported(AImgFileFormat::JPEG_IMAGE_FORMAT, AImgFormat::_16BITS)); + ASSERT_FALSE(AImgIsFormatSupported(AImgFileFormat::JPEG_IMAGE_FORMAT, AImgFormat::_32BITS)); +} + int main(int argc, char **argv) { AImgInitialise(); @@ -208,4 +243,4 @@ int main(int argc, char **argv) return retval; } -#endif +#endif \ No newline at end of file diff --git a/src_c/tests/png.cpp b/src_c/tests/png.cpp index 75939ef..32e3632 100644 --- a/src_c/tests/png.cpp +++ b/src_c/tests/png.cpp @@ -126,7 +126,8 @@ bool validateReadPNGFile(const std::string& path) return isEq; } -bool validateWritePNGFile(const std::string& path, void* encodeOptions, int32_t& err, std::vector& fileData) +bool validateWritePNGFile(const std::string& path, void* encodeOptions, int32_t& err, std::vector& fileData, + AImgFormat decodeFormat = AImgFormat::INVALID_FORMAT, AImgFormat writeFormat = AImgFormat::INVALID_FORMAT, AImgFormat expectedWrittenFormat = AImgFormat::INVALID_FORMAT) { err = AImgErrorCode::AIMG_SUCCESS; @@ -158,6 +159,22 @@ bool validateWritePNGFile(const std::string& path, void* encodeOptions, int32_t& uint32_t colourProfileLen; err = AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &fmt, &colourProfileLen); + + if (decodeFormat < 0) + { + decodeFormat = (AImgFormat)fmt; + } + if (writeFormat < 0) + { + writeFormat = decodeFormat; + } + if (expectedWrittenFormat < 0) + { + expectedWrittenFormat = writeFormat; + } + + AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + char profileName[30]; std::vector colourProfile(colourProfileLen); AImgGetColourProfile(img, profileName, colourProfile.data(), &colourProfileLen); @@ -165,7 +182,7 @@ bool validateWritePNGFile(const std::string& path, void* encodeOptions, int32_t& { imgData.resize(width*height*numChannels * bytesPerChannel, 78); - err = AImgDecodeImage(img, &imgData[0], AImgFormat::INVALID_FORMAT); + err = AImgDecodeImage(img, &imgData[0], decodeFormat); if(err == AImgErrorCode::AIMG_SUCCESS) { AImgClose(img); @@ -177,7 +194,8 @@ bool validateWritePNGFile(const std::string& path, void* encodeOptions, int32_t& AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); wImg = AImgGetAImg(AImgFileFormat::PNG_IMAGE_FORMAT); - err = AImgWriteImage(wImg, &imgData[0], width, height, fmt, profileName, colourProfile.data(), colourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, encodeOptions); + err = AImgWriteImage(wImg, &imgData[0], width, height, decodeFormat, writeFormat, profileName, colourProfile.data(), colourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, encodeOptions); + if(err == AImgErrorCode::AIMG_SUCCESS) { fileData.resize(tellCallback(callbackData)); @@ -196,9 +214,14 @@ bool validateWritePNGFile(const std::string& path, void* encodeOptions, int32_t& { imgData2.resize(width*height*numChannels*bytesPerChannel, 0); - err = AImgDecodeImage(img, &imgData2[0], AImgFormat::INVALID_FORMAT); + err = AImgDecodeImage(img, &imgData2[0], decodeFormat); if(err == AImgErrorCode::AIMG_SUCCESS) { + int32_t _, writtenFormat; + AImgGetInfo(img, &_, &_, &_, &_, &_, &writtenFormat, NULL); + if (writtenFormat != expectedWrittenFormat) + return false; + AImgClose(img); img = NULL; AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); @@ -546,7 +569,7 @@ TEST(Png, TestWriteFrom32Bit) AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); AImgHandle wImg = AImgGetAImg(AImgFileFormat::PNG_IMAGE_FORMAT); - AImgWriteImage(wImg, &fData[0], width, height, AImgFormat::RGBA32F, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + AImgWriteImage(wImg, &fData[0], width, height, AImgFormat::RGBA32F, AImgFormat::INVALID_FORMAT, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); AImgClose(wImg); seekCallback(callbackData, 0); @@ -598,7 +621,7 @@ TEST(Png, TestWriteFrom16BitFloat) AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); AImgHandle wImg = AImgGetAImg(AImgFileFormat::PNG_IMAGE_FORMAT); - AImgWriteImage(wImg, &fData[0], width, height, AImgFormat::RGBA16F, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + AImgWriteImage(wImg, &fData[0], width, height, AImgFormat::RGBA16F, AImgFormat::INVALID_FORMAT, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); AImgClose(wImg); seekCallback(callbackData, 0); @@ -620,6 +643,31 @@ TEST(Png, TestWriteFrom16BitFloat) AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); } +TEST(PNG, TestWriteConvert16) +{ + int32_t err; + std::vector fileData; + ASSERT_TRUE(validateWritePNGFile("/png/8-bit.png", NULL, err, fileData, + AImgFormat::INVALID_FORMAT, AImgFormat::RGB16U)); + ASSERT_EQ(err, AImgErrorCode::AIMG_SUCCESS); +} + +TEST(PNG, TestWriteConvert8) +{ + int32_t err; + std::vector fileData; + ASSERT_TRUE(validateWritePNGFile("/png/16-bit.png", NULL, err, fileData, + AImgFormat::RGB32F, AImgFormat::RGBA16U)); + ASSERT_EQ(err, AImgErrorCode::AIMG_SUCCESS); +} + +TEST(PNG, TestSupportedFormat) +{ + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::PNG_IMAGE_FORMAT, AImgFormat::_8BITS)); + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::PNG_IMAGE_FORMAT, AImgFormat::_16BITS)); + ASSERT_FALSE(AImgIsFormatSupported(AImgFileFormat::PNG_IMAGE_FORMAT, AImgFormat::_32BITS)); +} + #endif // HAVE_PNG int main(int argc, char **argv) diff --git a/src_c/tests/testCommon.cpp b/src_c/tests/testCommon.cpp index 608b87d..2684bc8 100644 --- a/src_c/tests/testCommon.cpp +++ b/src_c/tests/testCommon.cpp @@ -12,7 +12,7 @@ bool detectImage(const std::string& path, int32_t format) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); AImgHandle img = NULL; int32_t fileFormat = UNKNOWN_IMAGE_FORMAT; @@ -39,7 +39,7 @@ bool validateImageHeaders(const std::string & path, int32_t expectedWidth, int32 SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); AImgHandle img = NULL; AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); @@ -72,7 +72,7 @@ bool compareForceImageFormat(const std::string& path) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); int32_t err = AIMG_SUCCESS; @@ -145,7 +145,8 @@ bool compareForceImageFormat(const std::string& path) return true; } -void writeToFile(const std::string& path, int32_t width, int32_t height, void* data, int32_t inputFormat, int32_t fileFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen) +void writeToFile(const std::string& path, int32_t width, int32_t height, void* data, int32_t inputFormat, int32_t outputFormat, int32_t fileFormat, + const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen) { ReadCallback readCallback = NULL; WriteCallback writeCallback = NULL; @@ -153,13 +154,14 @@ void writeToFile(const std::string& path, int32_t width, int32_t height, void* d SeekCallback seekCallback = NULL; void* callbackData = NULL; - std::vector fData; + std::vector fData(1); AIGetResizableMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fData); AImgHandle wImg = AImgGetAImg(fileFormat); - AImgWriteImage(wImg, data, width, height, inputFormat, profileName, colourProfile, colourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, NULL); + AImgWriteImage(wImg, data, width, height, inputFormat, outputFormat, profileName, colourProfile, colourProfileLen, + writeCallback, tellCallback, seekCallback, callbackData, NULL); FILE* f = fopen(path.c_str(), "wb"); fwrite(&fData[0], 1, fData.size(), f); @@ -180,7 +182,7 @@ bool compareIccProfiles(const std::string & image1, const std::string & image2) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data1[0], data1.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data1[0], (int32_t)data1.size()); AImgHandle img1 = NULL; int32_t detectedFormat1; @@ -220,7 +222,7 @@ bool compareIccProfiles(const std::string & image1, const std::string & image2) ///////////////////// Read image 2 auto data2 = readFile(getImagesDir() + image2); - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data2[0], data2.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data2[0], (int32_t)data2.size()); AImgHandle img2 = NULL; int32_t detectedFormat2; @@ -278,7 +280,7 @@ bool compareIccProfiles(const std::string & image1, const std::string & image2) return true; } -void readWriteIcc(const std::string & path, const std::string & outPath, char *profileName, uint8_t **colourProfile, uint32_t *colourProfileLen) +void readWriteIcc(const std::string & path, const std::string & outPath, char *profileName, uint8_t **colourProfile, uint32_t *colourProfileLenPtr) { // Read auto data = readFile(getImagesDir() + path); @@ -289,7 +291,7 @@ void readWriteIcc(const std::string & path, const std::string & outPath, char *p SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], (int32_t)data.size()); AImgHandle img = NULL; int32_t detectedFormat; @@ -302,9 +304,9 @@ void readWriteIcc(const std::string & path, const std::string & outPath, char *p int32_t floatOrInt = 0; int32_t decodedImgFormat = 0; - AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &decodedImgFormat, colourProfileLen); - std::vector colourProfile_(*colourProfileLen); - AImgGetColourProfile(img, profileName, colourProfile_.data(), colourProfileLen); + AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &decodedImgFormat, colourProfileLenPtr); + std::vector colourProfile_(*colourProfileLenPtr); + AImgGetColourProfile(img, profileName, colourProfile_.data(), colourProfileLenPtr); *colourProfile = colourProfile_.data(); std::vector imgData; @@ -320,10 +322,25 @@ void readWriteIcc(const std::string & path, const std::string & outPath, char *p std::vector fileData; fileData.resize(width * height * numChannels * bytesPerChannel * 5); - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], (int32_t)fileData.size()); AImgHandle wImg = AImgGetAImg(detectedFormat); - AImgWriteImage(wImg, &imgData[0], width, height, decodedImgFormat, profileName, *colourProfile, *colourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, NULL); + + uint8_t * colourProfilePtr = NULL; + uint32_t colourProfileLen = 0; + + if (colourProfile != NULL) + { + colourProfilePtr = *colourProfile; + } + + if (colourProfileLenPtr != NULL) + { + colourProfileLen = *colourProfileLenPtr; + } + + AImgWriteImage(wImg, &imgData[0], width, height, decodedImgFormat, decodedImgFormat, + profileName, colourProfilePtr, colourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, NULL); FILE* f = fopen((getImagesDir() + outPath).c_str(), "wb"); fwrite(&fileData[0], 1, fileData.size(), f); diff --git a/src_c/tests/testCommon.h b/src_c/tests/testCommon.h index aae25b3..55debce 100644 --- a/src_c/tests/testCommon.h +++ b/src_c/tests/testCommon.h @@ -10,10 +10,10 @@ inline std::string getImagesDir() { std::string thisFile = __FILE__; - char dirSep = '/'; - #ifdef WIN32 - dirSep = '\\'; - #endif + char dirSep = '/'; +#ifdef WIN32 + dirSep = '\\'; +#endif size_t pos = thisFile.find_last_of(dirSep); size_t filenameLength = thisFile.length() - pos; @@ -33,7 +33,7 @@ std::vector readFile(const std::string& path) size_t size = ftell(f); fseek(f, 0, SEEK_SET); - std::vector retval(size/sizeof(T)); + std::vector retval(size / sizeof(T)); fread(&retval[0], 1, size, f); fclose(f); @@ -45,7 +45,8 @@ bool detectImage(const std::string& path, int32_t format); bool validateImageHeaders(const std::string & path, int32_t expectedWidth, int32_t expectedHeight, int32_t expectedNumChannels, int32_t expectedBytesPerChannel, int32_t expectedFloatOrInt, int32_t expectedFormat); bool compareForceImageFormat(const std::string& path); -void writeToFile(const std::string& path, int32_t width, int32_t height, void* data, int32_t inputFormat, int32_t fileFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen); +void writeToFile(const std::string& path, int32_t width, int32_t height, void* data, int32_t inputFormat, int32_t outputFormat, int32_t fileFormat, + const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen); void readWriteIcc(const std::string & path, const std::string & outPath, char *profileName, uint8_t **colourProfile, uint32_t *colourProfileLen); bool compareIccProfiles(const std::string & image1, const std::string & image2); diff --git a/src_c/tests/tga.cpp b/src_c/tests/tga.cpp index bbfdc7c..1d5af22 100644 --- a/src_c/tests/tga.cpp +++ b/src_c/tests/tga.cpp @@ -30,6 +30,78 @@ std::vector decodeTGAFile(const std::string & path) return data; } +void TestWriteTga(AImgFormat decodeFormat = AImgFormat::INVALID_FORMAT, AImgFormat writeFormat = AImgFormat::INVALID_FORMAT, AImgFormat expectedOutputFormat = AImgFormat::INVALID_FORMAT) +{ + auto data = readFile(getImagesDir() + "/tga/test.tga"); + + ReadCallback readCallback = NULL; + WriteCallback writeCallback = NULL; + TellCallback tellCallback = NULL; + SeekCallback seekCallback = NULL; + void* callbackData = NULL; + + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); + + AImgHandle img = NULL; + AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); + + int32_t width; + int32_t height; + int32_t numChannels; + int32_t bytesPerChannel; + int32_t floatOrInt; + int32_t fmt; + + AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &fmt, NULL); + + if (decodeFormat < 0) + { + decodeFormat = (AImgFormat)fmt; + } + if (writeFormat < 0) + { + writeFormat = decodeFormat; + } + if (expectedOutputFormat < 0) + { + expectedOutputFormat = writeFormat; + } + + AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + + std::vector imgData(width*height*numChannels * bytesPerChannel, 78); + + AImgDecodeImage(img, &imgData[0], decodeFormat); + + AImgClose(img); + AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); + + std::vector fileData(width * height * numChannels * bytesPerChannel * 5); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); + + AImgHandle wImg = AImgGetAImg(AImgFileFormat::TGA_IMAGE_FORMAT); + auto err = AImgWriteImage(wImg, &imgData[0], width, height, decodeFormat, writeFormat, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + ASSERT_EQ(err, AImgErrorCode::AIMG_SUCCESS); + AImgClose(wImg); + + seekCallback(callbackData, 0); + + AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); + + std::vector imgData2(width*height*numChannels*bytesPerChannel, 0); + AImgDecodeImage(img, &imgData2[0], decodeFormat); + + int32_t _, writtenFormat; + AImgGetInfo(img, &_, &_, &_, &_, &_, &writtenFormat, NULL); + ASSERT_EQ(writtenFormat, expectedOutputFormat); + + AImgClose(img); + AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); + + for (uint32_t i = 0; i < imgData2.size(); i++) + ASSERT_EQ(imgData[i], imgData2[i]); +} + TEST(TGA, TestDetectTGA) { ASSERT_TRUE(detectImage("/tga/test.tga", TGA_IMAGE_FORMAT)); @@ -166,54 +238,7 @@ TEST(TGA, TestReadTGAFile) TEST(TGA, TestWriteTGAFile) { - auto data = readFile(getImagesDir() + "/tga/test.tga"); - - ReadCallback readCallback = NULL; - WriteCallback writeCallback = NULL; - TellCallback tellCallback = NULL; - SeekCallback seekCallback = NULL; - void* callbackData = NULL; - - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &data[0], data.size()); - - AImgHandle img = NULL; - AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); - - int32_t width; - int32_t height; - int32_t numChannels; - int32_t bytesPerChannel; - int32_t floatOrInt; - int32_t fmt; - - AImgGetInfo(img, &width, &height, &numChannels, &bytesPerChannel, &floatOrInt, &fmt, NULL); - - std::vector imgData(width*height*numChannels * bytesPerChannel, 78); - - AImgDecodeImage(img, &imgData[0], AImgFormat::INVALID_FORMAT); - - AImgClose(img); - AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); - - std::vector fileData(width * height * numChannels * bytesPerChannel * 5); - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); - - AImgHandle wImg = AImgGetAImg(AImgFileFormat::TGA_IMAGE_FORMAT); - auto err = AImgWriteImage(wImg, &imgData[0], width, height, fmt, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); - ASSERT_EQ(err, AImgErrorCode::AIMG_SUCCESS); - AImgClose(wImg); - - seekCallback(callbackData, 0); - - AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); - - std::vector imgData2(width*height*numChannels*bytesPerChannel, 0); - AImgDecodeImage(img, &imgData2[0], AImgFormat::INVALID_FORMAT); - AImgClose(img); - AIDestroySimpleMemoryBufferCallbacks(readCallback, writeCallback, tellCallback, seekCallback, callbackData); - - for(uint32_t i = 0; i < imgData2.size(); i++) - ASSERT_EQ(imgData[i], imgData2[i]); + TestWriteTga(); } TEST(TGA, TestForceImageFormat) @@ -260,6 +285,19 @@ TEST(TGA, TestForceImageFormat) } +TEST(TGA, TestWriteConvert) +{ + TestWriteTga(AImgFormat::RGB16U, AImgFormat::INVALID_FORMAT, AImgFormat::RGB8U); + + TestWriteTga(AImgFormat::RGB16U, AImgFormat::RGB8U); +} + +TEST(TGA, TestSupportedFormat) +{ + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::TGA_IMAGE_FORMAT, AImgFormat::_8BITS)); + ASSERT_FALSE(AImgIsFormatSupported(AImgFileFormat::TGA_IMAGE_FORMAT, AImgFormat::_16BITS)); + ASSERT_FALSE(AImgIsFormatSupported(AImgFileFormat::TGA_IMAGE_FORMAT, AImgFormat::_32BITS)); +} int main(int argc, char **argv) { diff --git a/src_c/tests/tiff.cpp b/src_c/tests/tiff.cpp index 3b4a51b..272b477 100644 --- a/src_c/tests/tiff.cpp +++ b/src_c/tests/tiff.cpp @@ -21,7 +21,7 @@ bool compareTiffToPng(const std::string& name, bool convertSrgb = false) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &pngData[0], pngData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &pngData[0], (int32_t)pngData.size()); AImgHandle img = NULL; int32_t error = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); @@ -52,7 +52,7 @@ bool compareTiffToPng(const std::string& name, bool convertSrgb = false) auto tiffData = readFile(getImagesDir() + "/tiff/" + name); - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &tiffData[0], tiffData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &tiffData[0], (int32_t)tiffData.size()); error = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); if (error) @@ -96,10 +96,10 @@ bool compareTiffToPng(const std::string& name, bool convertSrgb = false) if (convertSrgb) { float f; - f = pngR / 255.0f; pngR = std::pow(f, 2.2f) * 255.0f; - f = pngG / 255.0f; pngG = std::pow(f, 2.2f) * 255.0f; - f = pngB / 255.0f; pngB = std::pow(f, 2.2f) * 255.0f; - f = pngA / 255.0f; pngA = std::pow(f, 2.2f) * 255.0f; + f = pngR / 255.0f; pngR = (uint8_t)(std::pow(f, 2.2f) * 255.0f); + f = pngG / 255.0f; pngG = (uint8_t)(std::pow(f, 2.2f) * 255.0f); + f = pngB / 255.0f; pngB = (uint8_t)(std::pow(f, 2.2f) * 255.0f); + f = pngA / 255.0f; pngA = (uint8_t)(std::pow(f, 2.2f) * 255.0f); } uint8_t tiffR = tiffImgData[(x + y*pngWidth) * 4 + 0]; @@ -121,8 +121,13 @@ bool compareTiffToPng(const std::string& name, bool convertSrgb = false) return true; } -bool testTiffWrite(int32_t testFormat) +bool testTiffWrite(int32_t testFormat, int32_t outputFormat = -1) { + if (outputFormat < 0) + { + outputFormat = testFormat; + } + auto pngData = readFile(getImagesDir() + "/tiff/8_bit_png.png"); ReadCallback readCallback = NULL; @@ -131,7 +136,7 @@ bool testTiffWrite(int32_t testFormat) SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &pngData[0], pngData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &pngData[0], (int32_t)pngData.size()); AImgHandle img = NULL; int32_t error = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &img, NULL); @@ -162,12 +167,15 @@ bool testTiffWrite(int32_t testFormat) /////////// - std::vector fileData(pngWidth*pngHeight*bytesPerChannel*numChannels * 10, 79); // 10x the raw data size, should be well enough space - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); + int32_t numChannels_out, bytesPerChannel_out, floatOrInt_out; + AIGetFormatDetails(outputFormat, &numChannels_out, &bytesPerChannel_out, &floatOrInt_out); + + std::vector fileData(pngWidth*pngHeight*bytesPerChannel_out*numChannels_out * 10, 79); // 10x the raw data size, should be well enough space + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], (int32_t)fileData.size()); AImgHandle wImg = AImgGetAImg(AImgFileFormat::TIFF_IMAGE_FORMAT); - error = AImgWriteImage(wImg, &pngImgData[0], pngWidth, pngHeight, testFormat, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); + error = AImgWriteImage(wImg, &pngImgData[0], pngWidth, pngHeight, testFormat, outputFormat, NULL, NULL, 0, writeCallback, tellCallback, seekCallback, callbackData, NULL); if (error) return false; @@ -183,7 +191,7 @@ bool testTiffWrite(int32_t testFormat) if (error) return false; - if (tiffImgFmt != testFormat) + if (tiffImgFmt != outputFormat) return false; if (tiffWidth != pngWidth || tiffHeight != pngHeight) @@ -191,7 +199,7 @@ bool testTiffWrite(int32_t testFormat) std::vector tiffImgData(pngWidth*pngHeight*numChannels*bytesPerChannel, 78); - error = AImgDecodeImage(img, &tiffImgData[0], AImgFormat::INVALID_FORMAT); + error = AImgDecodeImage(img, &tiffImgData[0], testFormat); if (error) return false; @@ -215,7 +223,7 @@ bool testColourProfileFromPngToTiff() SeekCallback seekCallback = NULL; void* callbackData = NULL; - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &pngData[0], pngData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &pngData[0], (int32_t)pngData.size()); AImgHandle pngImg = NULL; int32_t error = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &pngImg, NULL); @@ -250,7 +258,7 @@ bool testColourProfileFromPngToTiff() ////////////////////////////////////////////////////// Read tiff (only data) auto tiffData = readFile(getImagesDir() + "/tiff/ICC.tif"); - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &tiffData[0], tiffData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &tiffData[0], (int32_t)tiffData.size()); AImgHandle tiffImg1 = NULL; error = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &tiffImg1, NULL); @@ -283,11 +291,11 @@ bool testColourProfileFromPngToTiff() ////////////////////////////////////////////////////// Write tiff (data plus png colour profile) std::vector fileData(pngWidth*pngHeight*pngBytesPerChannel*pngNumChannels * 10, 79); // 10x the raw data size, should be well enough space - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], fileData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &fileData[0], (int32_t)fileData.size()); AImgHandle wImg = AImgGetAImg(AImgFileFormat::TIFF_IMAGE_FORMAT); - error = AImgWriteImage(wImg, &tiffImgData[0], pngWidth, pngHeight, AImgFormat::RGBA8U, "", pngColourProfile.data(), pngColourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, NULL); + error = AImgWriteImage(wImg, &tiffImgData[0], pngWidth, pngHeight, AImgFormat::RGBA8U, AImgFormat::INVALID_FORMAT, "", pngColourProfile.data(), pngColourProfileLen, writeCallback, tellCallback, seekCallback, callbackData, NULL); if (error) return false; @@ -304,7 +312,7 @@ bool testColourProfileFromPngToTiff() ////////////////////////////////////////////////////// Read written tiff (data and colour profile) tiffData = readFile(getImagesDir() + "/tiff/ICC_png.tif"); - AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &tiffData[0], tiffData.size()); + AIGetSimpleMemoryBufferCallbacks(&readCallback, &writeCallback, &tellCallback, &seekCallback, &callbackData, &tiffData[0], (int32_t)tiffData.size()); AImgHandle tiffImg2 = NULL; error = AImgOpen(readCallback, tellCallback, seekCallback, callbackData, &tiffImg2, NULL); @@ -418,17 +426,37 @@ TEST(TIFF, TestWrite8U) TEST(TIFF, TestWrite16U) { - testTiffWrite(AImgFormat::RGBA16U); + ASSERT_TRUE(testTiffWrite(AImgFormat::RGBA16U)); } TEST(TIFF, TestWrite16F) { - testTiffWrite(AImgFormat::RGBA16F); + ASSERT_TRUE(testTiffWrite(AImgFormat::RGBA16F)); } TEST(TIFF, TestWrite32F) { - testTiffWrite(AImgFormat::RGBA32F); + ASSERT_TRUE(testTiffWrite(AImgFormat::RGBA32F)); +} + + +/// +/// Read the png image in its current format, +/// save tiff image in the test format +/// +TEST(TIFF, TestWrite8Bits) +{ + ASSERT_TRUE(testTiffWrite(AImgFormat::RGB8U, AImgFormat::RGB8U)); +} + +TEST(TIFF, TestWrite16Bits) +{ + ASSERT_TRUE(testTiffWrite(AImgFormat::RGB8U, AImgFormat::RGB16U)); +} + +TEST(TIFF, TestWrite32bits) +{ + ASSERT_TRUE(testTiffWrite(AImgFormat::RGB8U, AImgFormat::RGB32F)); } TEST(TIFF, TestColourProfileFromPngToTiff) @@ -452,6 +480,13 @@ TEST(TIFF, TestCompareWithPngICCProfile) ASSERT_TRUE(compareIccProfiles("/png/ICC.png", "/tiff/ICC.tif")); } +TEST(TIFF, TestSupportedFormat) +{ + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::TIFF_IMAGE_FORMAT, AImgFormat::_8BITS)); + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::TIFF_IMAGE_FORMAT, AImgFormat::_16BITS)); + ASSERT_TRUE(AImgIsFormatSupported(AImgFileFormat::TIFF_IMAGE_FORMAT, AImgFormat::_32BITS)); +} + #endif // HAVE_TIFF int main(int argc, char **argv) diff --git a/src_c/tga.cpp b/src_c/tga.cpp index 7c83097..ef4a8cc 100644 --- a/src_c/tga.cpp +++ b/src_c/tga.cpp @@ -7,6 +7,7 @@ #include #include #define STBI_ONLY_TGA +#define STBI_ONLY_HDR #define STB_IMAGE_IMPLEMENTATION #include "extern/stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION @@ -14,19 +15,18 @@ namespace AImg { - namespace STBICallbacks { int readCallback(void * user, char * data, int size) { - CallbackData * callbackFunctions = (CallbackData *)user; + CallbackData * callbackFunctions = (CallbackData *)user; return callbackFunctions->readCallback(callbackFunctions->callbackData, (uint8_t *)data, size); } void seekCallback(void * user, int num_bytes) { CallbackData * callbackFunctions = (CallbackData *)user; - callbackFunctions->seekCallback(callbackFunctions->callbackData, num_bytes); + callbackFunctions->seekCallback(callbackFunctions->callbackData, num_bytes); } void writeFunc(void * user, void * data, int size) @@ -36,19 +36,27 @@ namespace AImg } } - AImgFormat getWhatFormatWillBeWrittenForDataTGA(int32_t inputFormat) + AImgFormat getWhatFormatWillBeWrittenForDataTGA(int32_t inputFormat, int32_t outputFormat) { + AIL_UNUSED_PARAM(outputFormat); + int32_t numChannels, bytesPerChannel, floatOrInt; AIGetFormatDetails(inputFormat, &numChannels, &bytesPerChannel, &floatOrInt); - if(floatOrInt == AImgFloatOrIntType::FITYPE_FLOAT) - return (AImgFormat) (AImgFormat::R8U + bytesPerChannel - 1); // convert to 8U version with same channelNum + switch (numChannels) + { + case 1: + return AImgFormat::R8U; + + case 2: + return AImgFormat::RG8U; - if(inputFormat >= AImgFormat::R8U && inputFormat <= AImgFormat::RGBA8U) - return (AImgFormat)inputFormat; + case 3: + return AImgFormat::RGB8U; - if(inputFormat >= AImgFormat::R16U && inputFormat <= AImgFormat::RGBA16U) - return (AImgFormat) (AImgFormat::R8U + bytesPerChannel - 1); // convert to 8U version with same channelNum + default: + break; + } return AImgFormat::INVALID_FORMAT; } @@ -70,7 +78,7 @@ namespace AImg bool hasCorrectColourMapType = (header[1] == 0 || header[1] == 1); bool hasCorrectImageType = (header[2] == 0 || header[2] == 1 || header[2] == 2 || header[2] == 3 || header[2] == 9 || header[2] == 10 || header[2] == 11); - uint16_t paletteLength = *(uint16_t *) (header + 5); + uint16_t paletteLength = *(uint16_t *)(header + 5); uint16_t width = *(uint16_t *)(header + 12); uint16_t height = *(uint16_t *)(header + 14); @@ -96,159 +104,165 @@ namespace AImg return TGA_IMAGE_FORMAT; } + bool isFormatSupportedByTga(int32_t format) + { + return format & AImgFormat::_8BITS; + } + class TGAFile : public AImgBase { - public: - CallbackData data; - int32_t numChannels, width, height; + public: + CallbackData data; + int32_t numChannels, width, height; - int32_t getDecodeFormat() + int32_t getDecodeFormat() + { + switch (numChannels) { - switch (numChannels) - { - case 1: - return AImgFormat::R8U; - case 2: - return AImgFormat::RG8U; - case 3: - return AImgFormat::RGB8U; - case 4: - return AImgFormat::RGBA8U; - default: - return AImgFormat::INVALID_FORMAT; - } + case 1: + return AImgFormat::R8U; + case 2: + return AImgFormat::RG8U; + case 3: + return AImgFormat::RGB8U; + case 4: + return AImgFormat::RGBA8U; + default: + return AImgFormat::INVALID_FORMAT; } + } - virtual int32_t getImageInfo(int32_t *width, int32_t *height, int32_t *numChannels, int32_t *bytesPerChannel, int32_t *floatOrInt, int32_t *decodedImgFormat, uint32_t *colourProfileLen) + virtual int32_t getImageInfo(int32_t *width, int32_t *height, int32_t *numChannels, int32_t *bytesPerChannel, int32_t *floatOrInt, int32_t *decodedImgFormat, uint32_t *colourProfileLen) + { + *width = this->width; + *height = this->height; + *numChannels = this->numChannels; + if (colourProfileLen != NULL) { - *width = this->width; - *height = this->height; - *numChannels = this->numChannels; - if(colourProfileLen != NULL) - { - *colourProfileLen = 0; - } - - *bytesPerChannel = 1; - *floatOrInt = AImgFloatOrIntType::FITYPE_INT; - *decodedImgFormat = getDecodeFormat(); + *colourProfileLen = 0; + } - return AImgErrorCode::AIMG_SUCCESS; + *bytesPerChannel = 1; + *floatOrInt = AImgFloatOrIntType::FITYPE_INT; + *decodedImgFormat = getDecodeFormat(); + + return AImgErrorCode::AIMG_SUCCESS; + } + + virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + { + if (colourProfile != NULL) + { + *colourProfileLen = 0; } - - virtual int32_t getColourProfile(char *profileName, uint8_t *colourProfile, uint32_t *colourProfileLen) + if (profileName != NULL) { - if(colourProfile != NULL) - { - *colourProfileLen = 0; - } - if(profileName != NULL) - { - std::strcpy(profileName, "no_profile"); - } + std::strcpy(profileName, "no_profile"); + } - return AImgErrorCode::AIMG_SUCCESS; + return AImgErrorCode::AIMG_SUCCESS; + } + + virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) + { + stbi_io_callbacks callbacks; + callbacks.read = STBICallbacks::readCallback; + callbacks.skip = STBICallbacks::seekCallback; + + uint8_t* loadedData = stbi_load_from_callbacks(&callbacks, &data, &width, &height, &numChannels, numChannels); + + if (!loadedData) + { + mErrorDetails = "[AImg::TGAImageLoader::TGAFile::decodeImage] stbi_load_from_callbacks failed!"; + return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; } - virtual int32_t decodeImage(void *realDestBuffer, int32_t forceImageFormat) + int32_t decodeFormat = getDecodeFormat(); + int32_t numChannels, bytesPerChannel, floatOrInt; + AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + + void* destBuffer = realDestBuffer; + + std::vector convertTmpBuffer(0); + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) { - stbi_io_callbacks callbacks; - callbacks.read = STBICallbacks::readCallback; - callbacks.skip = STBICallbacks::seekCallback; + convertTmpBuffer.resize(width * height * bytesPerChannel * numChannels); + destBuffer = &convertTmpBuffer[0]; + } - uint8_t* loadedData = stbi_load_from_callbacks(&callbacks, &data, &width, &height, &numChannels, numChannels); + memcpy(destBuffer, loadedData, width * height * bytesPerChannel * numChannels); + stbi_image_free(loadedData); - if(!loadedData) - { - mErrorDetails = "[AImg::TGAImageLoader::TGAFile::decodeImage] stbi_load_from_callbacks failed!"; - return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; - } + if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) + { + int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, width, height, decodeFormat, forceImageFormat); + if (err != AImgErrorCode::AIMG_SUCCESS) + return err; + } + + return AImgErrorCode::AIMG_SUCCESS; + } - int32_t decodeFormat = getDecodeFormat(); - int32_t numChannels, bytesPerChannel, floatOrInt; - AIGetFormatDetails(decodeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + virtual int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) + { + data.readCallback = readCallback; + data.tellCallback = tellCallback; + data.seekCallback = seekCallback; + data.callbackData = callbackData; + stbi_io_callbacks callbacks; + callbacks.read = STBICallbacks::readCallback; + callbacks.skip = STBICallbacks::seekCallback; + + int startingPosition = tellCallback(callbackData); + stbi_info_from_callbacks(&callbacks, &data, &width, &height, &numChannels); + seekCallback(callbackData, startingPosition); + + return AImgErrorCode::AIMG_SUCCESS; + } + virtual int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) + { + AIL_UNUSED_PARAM(profileName); + AIL_UNUSED_PARAM(colourProfile); + AIL_UNUSED_PARAM(colourProfileLen); + AIL_UNUSED_PARAM(encodingOptions); - void* destBuffer = realDestBuffer; + int32_t writeFormat = getWhatFormatWillBeWrittenForDataTGA(inputFormat, outputFormat); - std::vector convertTmpBuffer(0); - if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) - { - convertTmpBuffer.resize(width * height * bytesPerChannel * numChannels); - destBuffer = &convertTmpBuffer[0]; - } + std::vector convertBuffer(0); - memcpy(destBuffer, loadedData, width * height * bytesPerChannel * numChannels); - stbi_image_free(loadedData); + int32_t numChannels, bytesPerChannel, floatOrInt; + AIGetFormatDetails(writeFormat, &numChannels, &bytesPerChannel, &floatOrInt); + if (writeFormat != inputFormat) + { + convertBuffer.resize(width * height * numChannels * bytesPerChannel); - if (forceImageFormat != AImgFormat::INVALID_FORMAT && forceImageFormat != decodeFormat) - { - int32_t err = AImgConvertFormat(destBuffer, realDestBuffer, width, height, decodeFormat, forceImageFormat); - if(err != AImgErrorCode::AIMG_SUCCESS) - return err; - } + int32_t convertError = AImgConvertFormat(data, &convertBuffer[0], width, height, inputFormat, writeFormat); - return AImgErrorCode::AIMG_SUCCESS; + if (convertError != AImgErrorCode::AIMG_SUCCESS) + return convertError; + data = &convertBuffer[0]; } - virtual int32_t openImage(ReadCallback readCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData) - { - data.readCallback = readCallback; - data.tellCallback = tellCallback; - data.seekCallback = seekCallback; - data.callbackData = callbackData; - stbi_io_callbacks callbacks; - callbacks.read = STBICallbacks::readCallback; - callbacks.skip = STBICallbacks::seekCallback; - - int startingPosition = tellCallback(callbackData); - stbi_info_from_callbacks(&callbacks, &data, &width, &height, &numChannels); - seekCallback(callbackData, startingPosition); + CallbackData callbackFunctions; + callbackFunctions.tellCallback = tellCallback; + callbackFunctions.writeCallback = writeCallback; + callbackFunctions.seekCallback = seekCallback; + callbackFunctions.callbackData = callbackData; + int err = stbi_write_tga_to_func(&STBICallbacks::writeFunc, &callbackFunctions, width, height, numChannels, data); + if (err != 0) + { return AImgErrorCode::AIMG_SUCCESS; } - - virtual int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, - WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void* encodingOptions) + else { - AIL_UNUSED_PARAM(encodingOptions); - - int32_t writeFormat = getWhatFormatWillBeWrittenForDataTGA(inputFormat); - - std::vector convertBuffer(0); - - int32_t numChannels, bytesPerChannel, floatOrInt; - AIGetFormatDetails(writeFormat, &numChannels, &bytesPerChannel, &floatOrInt); - - if (writeFormat != inputFormat) - { - convertBuffer.resize(width * height * numChannels * bytesPerChannel); - - int32_t convertError = AImgConvertFormat(data, &convertBuffer[0], width, height, inputFormat, writeFormat); - - if (convertError != AImgErrorCode::AIMG_SUCCESS) - return convertError; - data = &convertBuffer[0]; - } - - CallbackData callbackFunctions; - callbackFunctions.tellCallback = tellCallback; - callbackFunctions.writeCallback = writeCallback; - callbackFunctions.seekCallback = seekCallback; - callbackFunctions.callbackData = callbackData; - - int err = stbi_write_tga_to_func(&STBICallbacks::writeFunc, &callbackFunctions, width, height, numChannels, data); - if (err != 0) - { - return AImgErrorCode::AIMG_SUCCESS; - } - else - { - mErrorDetails = "[AImg::TGAImageLoader::TGAFile::writeImage] stbi_write_tga_to_func failed!"; - return AImgErrorCode::AIMG_WRITE_FAILED_EXTERNAL; - } + mErrorDetails = "[AImg::TGAImageLoader::TGAFile::writeImage] stbi_write_tga_to_func failed!"; + return AImgErrorCode::AIMG_WRITE_FAILED_EXTERNAL; } + } }; AImgBase * TGAImageLoader::getAImg() @@ -256,10 +270,14 @@ namespace AImg return new TGAFile(); } - AImgFormat TGAImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat) + AImgFormat TGAImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) { - return getWhatFormatWillBeWrittenForDataTGA(inputFormat); + return getWhatFormatWillBeWrittenForDataTGA(inputFormat, outputFormat); } + bool TGAImageLoader::isFormatSupported(int32_t format) + { + return isFormatSupportedByTga(format); + } } -#endif +#endif \ No newline at end of file diff --git a/src_c/tga.h b/src_c/tga.h index 64d32c7..a2efe2d 100644 --- a/src_c/tga.h +++ b/src_c/tga.h @@ -7,7 +7,7 @@ namespace AImg { class TGAImageLoader : public ImageLoaderBase { - public: + public: virtual AImgBase * getAImg(); virtual int32_t initialise(); @@ -15,8 +15,9 @@ namespace AImg virtual std::string getFileExtension(); virtual int32_t getAImgFileFormatValue(); + virtual bool isFormatSupported(int32_t format); - virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat); + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat); }; } diff --git a/src_c/tiff.cpp b/src_c/tiff.cpp index c10af88..ad48d07 100644 --- a/src_c/tiff.cpp +++ b/src_c/tiff.cpp @@ -15,13 +15,21 @@ namespace AImg { - AImgFormat getWriteFormatTiff(int32_t inputFormat) + AImgFormat getWriteFormatTiff(int32_t inputFormat, int32_t outputFormat) { // Tiff can write all currently supported formats. Here we are just future-proofing in case we add some more formats later that tiff can't do - if (inputFormat > AImgFormat::INVALID_FORMAT && inputFormat <= AImgFormat::RGBA32F) + if (inputFormat <= AImgFormat::INVALID_FORMAT || inputFormat > AImgFormat::RGBA32F) + return AImgFormat::INVALID_FORMAT; + + if(inputFormat == outputFormat || outputFormat == AImgFormat::INVALID_FORMAT) return (AImgFormat)inputFormat; - return AImgFormat::INVALID_FORMAT; + int32_t outDepth = AIGetBitDepth(outputFormat); + + if(outDepth == AImgFormat::INVALID_FORMAT) + return (AImgFormat)inputFormat; + + return (AImgFormat)AIChangeBitDepth(inputFormat, outDepth); } struct tiffCallbackData @@ -40,7 +48,7 @@ namespace AImg { tiffCallbackData *callbacks = (tiffCallbackData *)st; - return callbacks->mReadCallback(callbacks->callbackData, (uint8_t *)buffer, size); + return callbacks->mReadCallback(callbacks->callbackData, (uint8_t *)buffer, (int32_t)size); } tsize_t tiff_Write(thandle_t st, tdata_t buffer, tsize_t size) @@ -48,7 +56,7 @@ namespace AImg tiffCallbackData *callbacks = (tiffCallbackData *)st; int32_t start = callbacks->mTellCallback(callbacks->callbackData); - callbacks->mWriteCallback(callbacks->callbackData, (uint8_t *)buffer, size); + callbacks->mWriteCallback(callbacks->callbackData, (uint8_t *)buffer, (int32_t)size); int32_t end = callbacks->mTellCallback(callbacks->callbackData); if (end > callbacks->furthestPositionWritten) @@ -104,7 +112,7 @@ namespace AImg } } - callbacks->mSeekCallback(callbacks->callbackData, finalPos); + callbacks->mSeekCallback(callbacks->callbackData, (int32_t)finalPos); return callbacks->mTellCallback(callbacks->callbackData); } @@ -128,26 +136,26 @@ namespace AImg { int mantissaBits = 16; int exponentBits = 7; - int maxExponent = pow(2, exponentBits) - 1; + int maxExponent = (int)(1 << exponentBits) - 1; int v = *((int *)src); int sign = v >> 23; - int exponent = (v >> mantissaBits) & (int)(pow(2, exponentBits) - 1); - int mantissa = v & (int)(pow(2, mantissaBits) - 1); + int exponent = (v >> mantissaBits) & ((int)(1 << exponentBits) - 1); + int mantissa = v & ((int)(1 << mantissaBits) - 1); if (exponent == 0) { if (mantissa != 0) { - while ((mantissa & (int)pow(2, mantissaBits)) == 0) + while ((mantissa & (int)(1 << mantissaBits)) == 0) { mantissa <<= 1; exponent--; } exponent++; - mantissa &= (int)(pow(2, mantissaBits) - 1); - exponent += 127 - (pow(2, exponentBits - 1) - 1); + mantissa &= ((int)(1 << mantissaBits) - 1); + exponent += 127 - (int)((1 << (exponentBits - 1)) - 1); } } @@ -157,7 +165,7 @@ namespace AImg } else { - exponent += 127 - (pow(2, exponentBits - 1) - 1); + exponent += 127 - (int)((1 << (exponentBits - 1)) - 1); } mantissa <<= (23 - mantissaBits); @@ -197,21 +205,21 @@ namespace AImg { // handle 24-bit float (lolwtf) if (bitsPerChannel == 24 && sampleFormat == SAMPLEFORMAT_IEEEFP) - return ((int32_t)AImgFormat::R32F) - 1 + channels; + return AImgFormat::_32BITS | AImgFormat::FLOAT_FORMAT | (AImgFormat::R << (channels - 1)); if (sampleFormat == SAMPLEFORMAT_IEEEFP) { if (bitsPerChannel == 16) - return ((int32_t)AImgFormat::R16F) - 1 + channels; + return AImgFormat::_16BITS | AImgFormat::FLOAT_FORMAT | (AImgFormat::R << (channels - 1)); else if (bitsPerChannel == 32) - return ((int32_t)AImgFormat::R32F) - 1 + channels; + return AImgFormat::_32BITS | AImgFormat::FLOAT_FORMAT | (AImgFormat::R << (channels - 1)); } else if (sampleFormat == SAMPLEFORMAT_UINT || sampleFormat == SAMPLEFORMAT_INT) { if (bitsPerChannel == 8) - return ((int32_t)AImgFormat::R8U) - 1 + channels; + return AImgFormat::_8BITS | (AImgFormat::R << (channels - 1)); else if (bitsPerChannel == 16) - return ((int32_t)AImgFormat::R16U) - 1 + channels; + return AImgFormat::_16BITS | (AImgFormat::R << (channels - 1)); } } @@ -279,7 +287,7 @@ namespace AImg destBuffer = &convertTmpBuffer[0]; } - uint32 stripsize = TIFFStripSize(tiff); + uint32 stripsize = (uint32)TIFFStripSize(tiff); int32_t bytesPerChannel = bitsPerChannel / 8; std::vector stripBuffer(stripsize); @@ -451,7 +459,7 @@ namespace AImg return AImgErrorCode::AIMG_LOAD_FAILED_EXTERNAL; } - bool hasSamplesPerPixel = TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &channels); + bool hasSamplesPerPixel = TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &channels) != 0; AImgErrorCode retval = AImgErrorCode::AIMG_SUCCESS; @@ -547,7 +555,7 @@ namespace AImg return retval; } - int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, + int32_t writeImage(void *data, int32_t width, int32_t height, int32_t inputFormat, int32_t outputFormat, const char *profileName, uint8_t *colourProfile, uint32_t colourProfileLen, WriteCallback writeCallback, TellCallback tellCallback, SeekCallback seekCallback, void *callbackData, void *encodingOptions) { // Suppress unused warning @@ -565,7 +573,7 @@ namespace AImg int32_t retval = AIMG_SUCCESS; - int32_t wFormat = getWriteFormatTiff(inputFormat); + int32_t wFormat = getWriteFormatTiff(inputFormat, outputFormat); if (wFormat == AImgFormat::INVALID_FORMAT) { mErrorDetails = "[AImg::TIFFImageLoader::TiffFile::writeImage] Cannot write this format to tiff."; // developers: see comment in getWriteFormatTiff @@ -576,6 +584,19 @@ namespace AImg int32_t numChannels, bytesPerChannel, floatOrInt; AIGetFormatDetails(wFormat, &numChannels, &bytesPerChannel, &floatOrInt); + // Convert + std::vector convertBuffer(0); + if (wFormat != inputFormat) + { + convertBuffer.resize(width * height * numChannels * bytesPerChannel); + + int32_t convertError = AImgConvertFormat(data, &convertBuffer[0], width, height, inputFormat, wFormat); + + if (convertError != AImgErrorCode::AIMG_SUCCESS) + return convertError; + data = &convertBuffer[0]; + } + TIFFSetField(wTiff, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(wTiff, TIFFTAG_IMAGELENGTH, height); TIFFSetField(wTiff, TIFFTAG_SAMPLESPERPIXEL, numChannels); @@ -615,7 +636,7 @@ namespace AImg TIFFClose(wTiff); - // Leave the pointer at the end of the file, libtiff does NOT do this by default. + // Leave the pointer at the end of the file, because libtiff doesn't... because it's a fantastic piece of software wCallbacks.mSeekCallback(wCallbacks.callbackData, wCallbacks.furthestPositionWritten); return retval; @@ -661,10 +682,17 @@ namespace AImg return AImgFileFormat::TIFF_IMAGE_FORMAT; } - AImgFormat TIFFImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat) + AImgFormat TIFFImageLoader::getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat) { - return getWriteFormatTiff(inputFormat); + return getWriteFormatTiff(inputFormat, outputFormat); + } + + bool TIFFImageLoader::isFormatSupported(int32_t format) + { + AIL_UNUSED_PARAM(format); + + return true; } } -#endif // HAVE_TIFF +#endif // HAVE_TIFF \ No newline at end of file diff --git a/src_c/tiff.h b/src_c/tiff.h index 76f6831..dc2d95c 100644 --- a/src_c/tiff.h +++ b/src_c/tiff.h @@ -16,7 +16,9 @@ namespace AImg virtual std::string getFileExtension(); virtual int32_t getAImgFileFormatValue(); - virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat); + virtual bool isFormatSupported(int32_t format); + + virtual AImgFormat getWhatFormatWillBeWrittenForData(int32_t inputFormat, int32_t outputFormat); }; } diff --git a/test_images/hdr/test-env.hdr b/test_images/hdr/test-env.hdr new file mode 100644 index 0000000..98015a7 Binary files /dev/null and b/test_images/hdr/test-env.hdr differ