├── tests ├── Stb.Native │ ├── app.rc │ ├── app.ico │ ├── resource.h │ ├── Stb.Native.cpp │ ├── Stdafx.h │ ├── Stdafx.cpp │ ├── AssemblyInfo.cpp │ ├── ReadMe.txt │ ├── Stb.Native.vcxproj │ └── Stb.Native.h ├── SafeStbImageSharp.Tests │ ├── Resources │ │ ├── DockPanes.jpg │ │ └── J7dAdPl.png │ ├── SafeStbImageSharp.Tests.csproj │ ├── Utility │ │ └── Res.cs │ └── Tests.cs └── StbImageSharp.Testing │ ├── app.config │ ├── packages.config │ ├── Properties │ └── AssemblyInfo.cs │ ├── StbImageSharp.Testing.csproj │ └── Program.cs ├── samples ├── SafeStbImageSharp.Samples.MonoGame │ ├── image.jpg │ ├── Program.cs │ ├── SafeStbImageSharp.Samples.MonoGame.csproj │ └── Game1.cs └── SafeStbImageSharp.Samples.WinForms │ ├── App.config │ ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx │ ├── Program.cs │ ├── Form1.cs │ ├── SafeStbImageSharp.Samples.WinForms.csproj │ ├── Form1.Designer.cs │ └── Form1.resx ├── src ├── AnimatedFrameResult.cs ├── ColorComponents.cs ├── SafeStbImageSharp.csproj ├── Utility │ ├── ArrayExtensions.cs │ ├── MathExtensions.cs │ ├── IOUtils.cs │ ├── FakePtr.cs │ └── Conversion.cs ├── ImageInfo.cs ├── Decoding │ ├── Decoder.cs │ ├── PsdDecoder.cs │ ├── TgaDecoder.cs │ ├── BmpDecoder.cs │ ├── ZLib.cs │ ├── GifDecoder.cs │ └── PngDecoder.cs └── ImageResult.cs ├── .github └── workflows │ └── build-and-publish.yml ├── README.md ├── .gitignore └── SafeStbImageSharp.sln /tests/Stb.Native/app.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/SafeStbImageSharp/HEAD/tests/Stb.Native/app.rc -------------------------------------------------------------------------------- /tests/Stb.Native/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/SafeStbImageSharp/HEAD/tests/Stb.Native/app.ico -------------------------------------------------------------------------------- /tests/Stb.Native/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by app.rc 4 | -------------------------------------------------------------------------------- /tests/Stb.Native/Stb.Native.cpp: -------------------------------------------------------------------------------- 1 | // This is the main DLL file. 2 | 3 | #include "stdafx.h" 4 | 5 | #include "Stb.Native.h" 6 | 7 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.MonoGame/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/SafeStbImageSharp/HEAD/samples/SafeStbImageSharp.Samples.MonoGame/image.jpg -------------------------------------------------------------------------------- /tests/SafeStbImageSharp.Tests/Resources/DockPanes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/SafeStbImageSharp/HEAD/tests/SafeStbImageSharp.Tests/Resources/DockPanes.jpg -------------------------------------------------------------------------------- /tests/SafeStbImageSharp.Tests/Resources/J7dAdPl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StbSharp/SafeStbImageSharp/HEAD/tests/SafeStbImageSharp.Tests/Resources/J7dAdPl.png -------------------------------------------------------------------------------- /tests/Stb.Native/Stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, 3 | // but are changed infrequently 4 | 5 | #pragma once 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/Stb.Native/Stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // Stb.Native.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/AnimatedFrameResult.cs: -------------------------------------------------------------------------------- 1 | namespace StbImageSharp 2 | { 3 | #if !STBSHARP_INTERNAL 4 | public 5 | #else 6 | internal 7 | #endif 8 | class AnimatedFrameResult : ImageResult 9 | { 10 | public int Delay 11 | { 12 | get; set; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ColorComponents.cs: -------------------------------------------------------------------------------- 1 | namespace StbImageSharp 2 | { 3 | #if !STBSHARP_INTERNAL 4 | public 5 | #else 6 | internal 7 | #endif 8 | enum ColorComponents 9 | { 10 | Grey = 1, 11 | GreyAlpha = 2, 12 | RedGreenBlue = 3, 13 | RedGreenBlueAlpha = 4 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace StbSharp.WinForms.Test 5 | { 6 | static class Program 7 | { 8 | /// 9 | /// The main entry point for the application. 10 | /// 11 | [STAThread] 12 | static void Main() 13 | { 14 | Application.EnableVisualStyles(); 15 | Application.SetCompatibleTextRenderingDefault(false); 16 | Application.Run(new Form1()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.MonoGame/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbImageSharp.Samples.MonoGame 4 | { 5 | /// 6 | /// The main class. 7 | /// 8 | public static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | using (var game = new Game1()) 17 | game.Run(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/StbImageSharp.Testing/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/SafeStbImageSharp.Tests/SafeStbImageSharp.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/SafeStbImageSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | SafeStbImageSharpTeam 4 | SafeStbImageSharp 5 | SafeStbImageSharp 6 | netstandard2.0;net45 7 | Safe C# port of the stb_image.h 8 | Public Domain 9 | https://github.com/StbSharp/SafeStbImageSharp 10 | 2.22.4 11 | 12 | 13 | 14 | true 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Utility/ArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbImageSharp.Utility 4 | { 5 | internal static class ArrayExtensions 6 | { 7 | public static void Clear(this Array array) 8 | { 9 | Array.Clear(array, 0, array.Length); 10 | } 11 | 12 | public static void Set(this T[] array, int index, int length, T value) 13 | { 14 | for(var i = index; i < index + length; ++i) 15 | { 16 | array[i] = value; 17 | } 18 | } 19 | 20 | public static void Set(this T[] array, T value) 21 | { 22 | array.Set(0, array.Length, value); 23 | } 24 | 25 | public static void memcpy(this T[] a, FakePtr b, int count) where T: new() 26 | { 27 | Array.Copy(b._array, b.Offset, a, 0, count); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/StbImageSharp.Testing/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/build-and-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build & Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | BuildAndPublish: 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | submodules: recursive 14 | - name: Setup .NET Core 15 | uses: actions/setup-dotnet@v1 16 | with: 17 | dotnet-version: '3.1.x' 18 | - name: Build SafeStbImageSharp 19 | run: dotnet build src\SafeStbImageSharp.csproj --configuration Release 20 | - name: Install NuGet 21 | uses: NuGet/setup-nuget@v1 22 | - name: Publish SafeStbImageSharp to NuGet 23 | run: nuget.exe push src\bin\Release\SafeStbImageSharp.*.nupkg ${{secrets.NUGET_APIKEY}} -Source https://api.nuget.org/v3/index.json -------------------------------------------------------------------------------- /src/Utility/MathExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace StbImageSharp.Utility 2 | { 3 | internal static class MathExtensions 4 | { 5 | public static int stbi__bitreverse16(int n) 6 | { 7 | n = (int)(((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1)); 8 | n = (int)(((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2)); 9 | n = (int)(((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4)); 10 | n = (int)(((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8)); 11 | return (int)(n); 12 | } 13 | 14 | public static int stbi__bit_reverse(int v, int bits) 15 | { 16 | return (int)(stbi__bitreverse16((int)(v)) >> (16 - bits)); 17 | } 18 | 19 | public static uint _lrotl(uint x, int y) 20 | { 21 | return (x << y) | (x >> (32 - y)); 22 | } 23 | 24 | public static int ToReqComp(this ColorComponents? requiredComponents) 25 | { 26 | return requiredComponents != null ? (int)requiredComponents.Value : 0; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.MonoGame/SafeStbImageSharp.Samples.MonoGame.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | 5 | 6 | StbImageLib.Samples.MonoGame 7 | StbImageLib.Samples.MonoGame 8 | net45 9 | bin\MonoGame\$(Configuration) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/ImageInfo.cs: -------------------------------------------------------------------------------- 1 | using StbImageSharp.Decoding; 2 | using System.IO; 3 | 4 | namespace StbImageSharp 5 | { 6 | #if !STBSHARP_INTERNAL 7 | public 8 | #else 9 | internal 10 | #endif 11 | struct ImageInfo 12 | { 13 | public int Width; 14 | public int Height; 15 | public ColorComponents ColorComponents; 16 | public int BitsPerChannel; 17 | 18 | public static ImageInfo? FromStream(Stream stream) 19 | { 20 | ImageInfo? info = null; 21 | 22 | if (JpgDecoder.Test(stream)) 23 | { 24 | info = JpgDecoder.Info(stream); 25 | } 26 | else if (PngDecoder.Test(stream)) 27 | { 28 | info = PngDecoder.Info(stream); 29 | } 30 | else if (BmpDecoder.Test(stream)) 31 | { 32 | info = BmpDecoder.Info(stream); 33 | } 34 | else if (GifDecoder.Test(stream)) 35 | { 36 | info = GifDecoder.Info(stream); 37 | } 38 | else if (PsdDecoder.Test(stream)) 39 | { 40 | info = PsdDecoder.Info(stream); 41 | } 42 | else if (TgaDecoder.Test(stream)) 43 | { 44 | info = TgaDecoder.Info(stream); 45 | } 46 | 47 | return info; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace StbSharp.WinForms.Test.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Utility/IOUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace StbImageSharp.Utility 5 | { 6 | internal static class IOUtils 7 | { 8 | public static void Rewind(this Stream stream) 9 | { 10 | stream.Seek(0, SeekOrigin.Begin); 11 | } 12 | 13 | public static byte stbi__get8(this Stream s) 14 | { 15 | int b = s.ReadByte(); 16 | if (b == -1) 17 | { 18 | throw new Exception("EOF"); 19 | } 20 | 21 | return (byte)b; 22 | } 23 | 24 | public static int stbi__get16be(this Stream s) 25 | { 26 | int z = s.stbi__get8(); 27 | return (z << 8) + s.stbi__get8(); 28 | } 29 | 30 | public static uint stbi__get32be(this Stream s) 31 | { 32 | uint z = (uint)stbi__get16be(s); 33 | return (uint)((z << 16) + stbi__get16be(s)); 34 | } 35 | 36 | public static int stbi__get16le(this Stream s) 37 | { 38 | int z = s.stbi__get8(); 39 | return z + (s.stbi__get8() << 8); 40 | } 41 | 42 | public static uint stbi__get32le(this Stream s) 43 | { 44 | uint z = (uint)(stbi__get16le(s)); 45 | return (uint)(z + (stbi__get16le(s) << 16)); 46 | } 47 | 48 | public static void stbi__skip(this Stream s, int skip) 49 | { 50 | s.Seek(skip, SeekOrigin.Current); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Stb.Native/AssemblyInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace System; 4 | using namespace System::Reflection; 5 | using namespace System::Runtime::CompilerServices; 6 | using namespace System::Runtime::InteropServices; 7 | using namespace System::Security::Permissions; 8 | 9 | // 10 | // General Information about an assembly is controlled through the following 11 | // set of attributes. Change these attribute values to modify the information 12 | // associated with an assembly. 13 | // 14 | [assembly:AssemblyTitleAttribute(L"StbNative")]; 15 | [assembly:AssemblyDescriptionAttribute(L"")]; 16 | [assembly:AssemblyConfigurationAttribute(L"")]; 17 | [assembly:AssemblyCompanyAttribute(L"")]; 18 | [assembly:AssemblyProductAttribute(L"StbNative")]; 19 | [assembly:AssemblyCopyrightAttribute(L"Copyright (c) 2017")]; 20 | [assembly:AssemblyTrademarkAttribute(L"")]; 21 | [assembly:AssemblyCultureAttribute(L"")]; 22 | 23 | // 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the value or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | 34 | [assembly:AssemblyVersionAttribute("1.0.*")]; 35 | 36 | [assembly:ComVisible(false)]; 37 | 38 | [assembly:CLSCompliantAttribute(true)]; -------------------------------------------------------------------------------- /tests/StbImageSharp.Testing/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("StbImageSharp.Testing")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("StbImageSharp.Testing")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("1cba18d9-3b9b-4f6a-8729-3e88abc98c33")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("StbSharp.WinForms.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("StbSharp.WinForms.Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("69f55f52-5cab-4604-b6c3-3ed1180bf387")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Decoding/Decoder.cs: -------------------------------------------------------------------------------- 1 | using StbImageSharp.Utility; 2 | using System; 3 | using System.IO; 4 | 5 | namespace StbImageSharp.Decoding 6 | { 7 | #if !STBSHARP_INTERNAL 8 | public 9 | #else 10 | internal 11 | #endif 12 | class Decoder 13 | { 14 | public const int STBI__SCAN_load = 0; 15 | public const int STBI__SCAN_type = 1; 16 | public const int STBI__SCAN_header = 2; 17 | protected int img_x = 0; 18 | protected int img_y = 0; 19 | protected int img_n = 0; 20 | 21 | public Stream Stream { get; private set; } 22 | 23 | protected Decoder(Stream stream) 24 | { 25 | Stream = stream ?? throw new ArgumentNullException(nameof(stream)); 26 | } 27 | 28 | protected uint stbi__get32be() 29 | { 30 | return Stream.stbi__get32be(); 31 | } 32 | 33 | protected int stbi__get16be() 34 | { 35 | return Stream.stbi__get16be(); 36 | } 37 | 38 | protected uint stbi__get32le() 39 | { 40 | return Stream.stbi__get32le(); 41 | } 42 | 43 | protected int stbi__get16le() 44 | { 45 | return Stream.stbi__get16le(); 46 | } 47 | 48 | protected byte stbi__get8() 49 | { 50 | return Stream.stbi__get8(); 51 | } 52 | 53 | protected bool stbi__getn(byte[] buffer, int offset, int count) 54 | { 55 | var read = Stream.Read(buffer, offset, count); 56 | 57 | return read == count; 58 | } 59 | 60 | protected void stbi__skip(int count) 61 | { 62 | Stream.stbi__skip(count); 63 | } 64 | 65 | protected bool stbi__at_eof() 66 | { 67 | return Stream.Position == Stream.Length; 68 | } 69 | 70 | internal static void stbi__err(string message) 71 | { 72 | throw new Exception(message); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/Stb.Native/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | DYNAMIC LINK LIBRARY : Stb.Native Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this Stb.Native DLL for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your Stb.Native application. 9 | 10 | Stb.Native.vcxproj 11 | This is the main project file for VC++ projects generated using an Application Wizard. 12 | It contains information about the version of Visual C++ that generated the file, and 13 | information about the platforms, configurations, and project features selected with the 14 | Application Wizard. 15 | 16 | Stb.Native.vcxproj.filters 17 | This is the filters file for VC++ projects generated using an Application Wizard. 18 | It contains information about the association between the files in your project 19 | and the filters. This association is used in the IDE to show grouping of files with 20 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 21 | "Source Files" filter). 22 | 23 | Stb.Native.cpp 24 | This is the main DLL source file. 25 | 26 | Stb.Native.h 27 | This file contains a class declaration. 28 | 29 | AssemblyInfo.cpp 30 | Contains custom attributes for modifying assembly metadata. 31 | 32 | ///////////////////////////////////////////////////////////////////////////// 33 | Other notes: 34 | 35 | AppWizard uses "TODO:" to indicate parts of the source code you 36 | should add to or customize. 37 | 38 | ///////////////////////////////////////////////////////////////////////////// 39 | -------------------------------------------------------------------------------- /tests/SafeStbImageSharp.Tests/Utility/Res.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | 4 | namespace StbImageSharp.Tests.Utility 5 | { 6 | /// 7 | /// Resource utility 8 | /// 9 | public static class Res 10 | { 11 | /// 12 | /// Open assembly resource stream by relative name 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static Stream OpenResourceStream(this Assembly assembly, string assetName) 18 | { 19 | var path = assembly.GetName().Name + ".Resources." + assetName; 20 | 21 | // Once you figure out the name, pass it in as the argument here. 22 | var stream = assembly.GetManifestResourceStream(path); 23 | 24 | return stream; 25 | } 26 | 27 | /// 28 | /// Reads assembly resource as byte array by relative name 29 | /// 30 | /// 31 | /// 32 | /// 33 | public static byte[] ReadResourceAsBytes(this Assembly assembly, string assetName) 34 | { 35 | var ms = new MemoryStream(); 36 | using (var input = assembly.OpenResourceStream(assetName)) 37 | { 38 | input.CopyTo(ms); 39 | 40 | return ms.ToArray(); 41 | } 42 | } 43 | 44 | /// 45 | /// Reads assembly resource as string by relative name 46 | /// 47 | /// 48 | /// 49 | /// 50 | public static string ReadResourceAsString(this Assembly assembly, string assetName) 51 | { 52 | string result; 53 | using (var input = assembly.OpenResourceStream(assetName)) 54 | { 55 | using (var textReader = new StreamReader(input)) 56 | { 57 | result = textReader.ReadToEnd(); 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ImageResult.cs: -------------------------------------------------------------------------------- 1 | using StbImageSharp.Decoding; 2 | using System.IO; 3 | 4 | namespace StbImageSharp 5 | { 6 | #if !STBSHARP_INTERNAL 7 | public 8 | #else 9 | internal 10 | #endif 11 | class ImageResult 12 | { 13 | public int Width { get; set; } 14 | public int Height { get; set; } 15 | public ColorComponents ColorComponents { get; set; } 16 | public ColorComponents SourceComponents { get; set; } 17 | 18 | /// 19 | /// Either 8 or 16 20 | /// 21 | public int BitsPerChannel { get; set; } 22 | public byte[] Data { get; set; } 23 | 24 | public static ImageResult FromMemory(byte[] data, ColorComponents? requiredComponents = null, bool use8BitsPerChannel = true) 25 | { 26 | using (var stream = new MemoryStream(data)) 27 | { 28 | return FromStream(stream, requiredComponents, use8BitsPerChannel); 29 | } 30 | } 31 | 32 | public static ImageResult FromStream(Stream stream, ColorComponents? requiredComponents = null, bool use8BitsPerChannel = true) 33 | { 34 | ImageResult result = null; 35 | if (JpgDecoder.Test(stream)) 36 | { 37 | result = JpgDecoder.Decode(stream, requiredComponents); 38 | } 39 | else if (PngDecoder.Test(stream)) 40 | { 41 | result = PngDecoder.Decode(stream, requiredComponents); 42 | } 43 | else if (BmpDecoder.Test(stream)) 44 | { 45 | result = BmpDecoder.Decode(stream, requiredComponents); 46 | } 47 | else if (GifDecoder.Test(stream)) 48 | { 49 | result = GifDecoder.Decode(stream, requiredComponents); 50 | } 51 | else if (PsdDecoder.Test(stream)) 52 | { 53 | result = PsdDecoder.Decode(stream, requiredComponents); 54 | } 55 | else if (TgaDecoder.Test(stream)) 56 | { 57 | result = TgaDecoder.Decode(stream, requiredComponents); 58 | } 59 | 60 | if (result == null) 61 | { 62 | Decoder.stbi__err("unknown image type"); 63 | } 64 | 65 | if (use8BitsPerChannel && result.BitsPerChannel != 8) 66 | { 67 | result.Data = Conversion.stbi__convert_16_to_8(result.Data, result.Width, result.Height, (int)result.ColorComponents); 68 | } 69 | 70 | return result; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /tests/SafeStbImageSharp.Tests/Tests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using StbImageSharp.Tests.Utility; 3 | using System.IO; 4 | using System.Reflection; 5 | 6 | namespace StbImageSharp.Tests 7 | { 8 | [TestFixture] 9 | public class Tests 10 | { 11 | private static readonly Assembly _assembly = typeof(Tests).Assembly; 12 | 13 | [TestCase("DockPanes.jpg", 2000, 609, 406, ColorComponents.RedGreenBlue, false)] 14 | [TestCase("J7dAdPl.png", 1000, 182, 169, ColorComponents.RedGreenBlueAlpha, false)] 15 | public void Info(string filename, int headerSize, int width, int height, ColorComponents colorComponents, bool is16bit) 16 | { 17 | ImageInfo? result; 18 | 19 | var data = new byte[headerSize]; 20 | using (var stream = _assembly.OpenResourceStream(filename)) 21 | { 22 | stream.Read(data, 0, data.Length); 23 | } 24 | 25 | using (var stream = new MemoryStream(data)) 26 | { 27 | result = ImageInfo.FromStream(stream); 28 | } 29 | 30 | Assert.IsNotNull(result); 31 | 32 | var info = result.Value; 33 | Assert.AreEqual(info.Width, width); 34 | Assert.AreEqual(info.Height, height); 35 | Assert.AreEqual(info.ColorComponents, colorComponents); 36 | Assert.AreEqual(info.BitsPerChannel, is16bit ? 16 : 8); 37 | } 38 | 39 | [TestCase("DockPanes.jpg", 609, 406, ColorComponents.RedGreenBlue, false)] 40 | [TestCase("J7dAdPl.png", 182, 169, ColorComponents.RedGreenBlueAlpha, false)] 41 | public void Load(string filename, int width, int height, ColorComponents colorComponents, bool is16bit) 42 | { 43 | ImageResult result; 44 | 45 | using (var stream = _assembly.OpenResourceStream(filename)) 46 | { 47 | result = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha); 48 | } 49 | 50 | Assert.IsNotNull(result); 51 | 52 | Assert.AreEqual(result.Width, width); 53 | Assert.AreEqual(result.Height, height); 54 | Assert.AreEqual(result.SourceComponents, colorComponents); 55 | Assert.AreEqual(result.BitsPerChannel, is16bit ? 16 : 8); 56 | Assert.IsNotNull(result.Data); 57 | Assert.AreEqual(result.Data.Length, result.Width * result.Height * (int)result.ColorComponents); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.MonoGame/Game1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.Xna.Framework; 4 | using Microsoft.Xna.Framework.Graphics; 5 | 6 | namespace StbImageSharp.Samples.MonoGame 7 | { 8 | /// 9 | /// This is the main type for your game. 10 | /// 11 | public class Game1 : Game 12 | { 13 | private const int FontBitmapWidth = 1024; 14 | private const int FontBitmapHeight = 1024; 15 | 16 | GraphicsDeviceManager _graphics; 17 | SpriteBatch _spriteBatch; 18 | 19 | private Texture2D _image; 20 | 21 | public Game1() 22 | { 23 | _graphics = new GraphicsDeviceManager(this) 24 | { 25 | PreferredBackBufferWidth = 1400, 26 | PreferredBackBufferHeight = 960 27 | }; 28 | 29 | Content.RootDirectory = "Content"; 30 | IsMouseVisible = true; 31 | Window.AllowUserResizing = true; 32 | } 33 | 34 | /// 35 | /// LoadContent will be called once per game and is the place to load 36 | /// all of your content. 37 | /// 38 | protected override void LoadContent() 39 | { 40 | // Create a new SpriteBatch, which can be used to draw textures. 41 | _spriteBatch = new SpriteBatch(GraphicsDevice); 42 | 43 | // TODO: use this.Content to load your game content here 44 | 45 | // Load image data into memory 46 | var path = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location); 47 | path = Path.Combine(path, "image.jpg"); 48 | 49 | using (var stream = File.OpenRead(path)) 50 | { 51 | var image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha); 52 | _image = new Texture2D(GraphicsDevice, image.Width, image.Height, false, SurfaceFormat.Color); 53 | _image.SetData(image.Data); 54 | } 55 | 56 | GC.Collect(); 57 | } 58 | 59 | /// 60 | /// This is called when the game should draw itself. 61 | /// 62 | /// Provides a snapshot of timing values. 63 | protected override void Draw(GameTime gameTime) 64 | { 65 | GraphicsDevice.Clear(Color.CornflowerBlue); 66 | 67 | // TODO: Add your drawing code here 68 | _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend); 69 | 70 | _spriteBatch.Draw(_image, Vector2.Zero); 71 | 72 | _spriteBatch.End(); 73 | 74 | base.Draw(gameTime); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Form1.cs: -------------------------------------------------------------------------------- 1 | using StbImageSharp; 2 | using System; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.IO; 6 | using System.Runtime.InteropServices; 7 | using System.Windows.Forms; 8 | 9 | namespace StbSharp.WinForms.Test 10 | { 11 | public partial class Form1 : Form 12 | { 13 | private string _fileName; 14 | private ImageResult _loadedImage; 15 | 16 | public Form1() 17 | { 18 | InitializeComponent(); 19 | } 20 | 21 | private void button1_Click(object sender, EventArgs e) 22 | { 23 | try 24 | { 25 | using (var dlg = new OpenFileDialog()) 26 | { 27 | dlg.Filter = 28 | "PNG Files (*.png)|*.png|JPEG Files (*.jpg)|*.jpg|BMP Files (*.bmp)|*.bmp|PSD Files (*.psd)|*.psd|TGA Files (*.tga)|*.tga|GIF Files (*.gif)|*.gif|All Files (*.*)|*.*"; 29 | if (dlg.ShowDialog() != DialogResult.OK) 30 | { 31 | return; 32 | } 33 | 34 | _fileName = dlg.FileName; 35 | 36 | var bytes = File.ReadAllBytes(_fileName); 37 | 38 | using (var stream = File.OpenRead(_fileName)) 39 | { 40 | _loadedImage = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha); 41 | } 42 | SetImage(); 43 | } 44 | } 45 | catch (Exception ex) 46 | { 47 | MessageBox.Show("Error", ex.Message); 48 | } 49 | } 50 | 51 | private void SetImage() 52 | { 53 | // Convert to bgra 54 | var data = new byte[_loadedImage.Data.Length]; 55 | Array.Copy(_loadedImage.Data, data, data.Length); 56 | 57 | for (var i = 0; i < _loadedImage.Width*_loadedImage.Height; ++i) 58 | { 59 | var r = data[i*4]; 60 | var g = data[i*4 + 1]; 61 | var b = data[i*4 + 2]; 62 | var a = data[i*4 + 3]; 63 | 64 | 65 | data[i*4] = b; 66 | data[i*4 + 1] = g; 67 | data[i*4 + 2] = r; 68 | data[i*4 + 3] = a; 69 | } 70 | 71 | // Convert to Bitmap 72 | var bmp = new Bitmap(_loadedImage.Width, _loadedImage.Height, PixelFormat.Format32bppArgb); 73 | var bmpData = bmp.LockBits(new Rectangle(0, 0, _loadedImage.Width, _loadedImage.Height), ImageLockMode.WriteOnly, 74 | bmp.PixelFormat); 75 | 76 | Marshal.Copy(data, 0, bmpData.Scan0, bmpData.Stride*bmp.Height); 77 | bmp.UnlockBits(bmpData); 78 | 79 | pictureBox1.Image = bmp; 80 | _numericWidth.Value = _loadedImage.Width; 81 | _numericHeight.Value = _loadedImage.Height; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace StbSharp.WinForms.Test.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("StbSharp.WinForms.Test.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Utility/FakePtr.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StbImageSharp.Utility 4 | { 5 | internal struct FakePtr where T: new() 6 | { 7 | public static FakePtr Null = new FakePtr(null); 8 | 9 | public readonly T[] _array; 10 | 11 | public int Offset; 12 | 13 | public bool IsNull 14 | { 15 | get 16 | { 17 | return _array == null; 18 | } 19 | } 20 | 21 | public T this[int index] 22 | { 23 | get 24 | { 25 | return _array[Offset + index]; 26 | } 27 | 28 | set 29 | { 30 | _array[Offset + index] = value; 31 | } 32 | } 33 | 34 | public T this[long index] 35 | { 36 | get 37 | { 38 | return _array[Offset + index]; 39 | } 40 | 41 | set 42 | { 43 | _array[Offset + index] = value; 44 | } 45 | } 46 | 47 | public T Value 48 | { 49 | get 50 | { 51 | return this[0]; 52 | } 53 | 54 | set 55 | { 56 | this[0] = value; 57 | } 58 | } 59 | 60 | public FakePtr(FakePtr ptr, int offset) 61 | { 62 | Offset = ptr.Offset + offset; 63 | _array = ptr._array; 64 | } 65 | 66 | public FakePtr(T[] data, int offset) 67 | { 68 | Offset = offset; 69 | _array = data; 70 | } 71 | 72 | public FakePtr(T[] data): this(data, 0) 73 | { 74 | } 75 | 76 | public FakePtr(T value) 77 | { 78 | Offset = 0; 79 | _array = new T[1]; 80 | _array[0] = value; 81 | } 82 | 83 | public void Clear(int count) 84 | { 85 | Array.Clear(_array, Offset, count); 86 | } 87 | 88 | public T GetAndIncrease() 89 | { 90 | var result = _array[Offset]; 91 | ++Offset; 92 | 93 | return result; 94 | } 95 | 96 | public void SetAndIncrease(T value) 97 | { 98 | _array[Offset] = value; 99 | ++Offset; 100 | } 101 | 102 | public void Set(T value) 103 | { 104 | _array[Offset] = value; 105 | } 106 | 107 | public static FakePtr operator +(FakePtr p, int offset) 108 | { 109 | return new FakePtr(p._array) { Offset = p.Offset + offset }; 110 | } 111 | 112 | public static FakePtr operator -(FakePtr p, int offset) 113 | { 114 | return p + -offset; 115 | } 116 | 117 | public static FakePtr operator +(FakePtr p, uint offset) 118 | { 119 | return p + (int)offset; 120 | } 121 | 122 | public static FakePtr operator -(FakePtr p, uint offset) 123 | { 124 | return p - (int)offset; 125 | } 126 | 127 | public static FakePtr operator +(FakePtr p, long offset) 128 | { 129 | return p + (int)offset; 130 | } 131 | 132 | public static FakePtr operator -(FakePtr p, long offset) 133 | { 134 | return p - (int)offset; 135 | } 136 | 137 | public static FakePtr operator ++(FakePtr p) 138 | { 139 | return p + 1; 140 | } 141 | 142 | public static FakePtr CreateWithSize(int size) 143 | { 144 | var result = new FakePtr(new T[size]); 145 | 146 | for (int i = 0; i < size; ++i) 147 | { 148 | result[i] = new T(); 149 | } 150 | 151 | return result; 152 | } 153 | 154 | public static FakePtr CreateWithSize(long size) 155 | { 156 | return CreateWithSize((int)size); 157 | } 158 | 159 | public static FakePtr Create() 160 | { 161 | return CreateWithSize(1); 162 | } 163 | 164 | public void memset(T value, int count) 165 | { 166 | _array.Set(Offset, count, value); 167 | } 168 | 169 | public void memcpy(FakePtr b, int count) 170 | { 171 | Array.Copy(b._array, b.Offset, _array, Offset, count); 172 | } 173 | 174 | public void memcpy(T[] b, int count) 175 | { 176 | Array.Copy(b, 0, _array, Offset, count); 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SafeStbImageSharp 2 | [![NuGet](https://img.shields.io/nuget/v/SafeStbImageSharp.svg)](https://www.nuget.org/packages/SafeStbImageSharp/) 3 | ![Build & Publish](https://github.com/StbSharp/SafeStbImageSharp/workflows/Build%20&%20Publish/badge.svg) 4 | [![Chat](https://img.shields.io/discord/628186029488340992.svg)](https://discord.gg/ZeHxhCY) 5 | 6 | SafeStbImageSharp is safe and refactored version of [StbImageSharp](https://github.com/StbSharp/StbImageSharp). 7 | 8 | # Adding Reference 9 | There are two ways of referencing SafeStbImageSharp in the project: 10 | 1. Through nuget: https://www.nuget.org/packages/SafeStbImageSharp/ 11 | 2. As submodule: 12 | 13 | a. `git submodule add https://github.com/StbSharp/SafeStbImageSharp.git` 14 | 15 | b. Now there are two options: 16 | 17 | * Add SafeStbImageSharp/src/SafeStbImageSharp/SafeStbImageSharp.csproj to the solution 18 | 19 | * Include *.cs from SafeStbImageSharp/src/SafeStbImageSharp directly in the project. In this case, it might make sense to add STBSHARP_INTERNAL build compilation symbol to the project, so SafeStbImageSharp classes would become internal. 20 | 21 | # Usage 22 | Following code loads image from stream and converts it to 32-bit RGBA: 23 | ```c# 24 | ImageResult image; 25 | using (var stream = File.OpenRead(path)) 26 | { 27 | image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha); 28 | } 29 | ``` 30 | 31 | If you are writing MonoGame application and would like to convert that data to the Texture2D. It could be done following way: 32 | ```c# 33 | Texture2D texture = new Texture2D(GraphicsDevice, image.Width, image.Height, false, SurfaceFormat.Color); 34 | texture.SetData(image.Data); 35 | ``` 36 | 37 | Or if you are writing WinForms app and would like StbSharp resulting bytes to be converted to the Bitmap. The sample code is: 38 | ```c# 39 | byte[] data = image.Data; 40 | // Convert rgba to bgra 41 | for (int i = 0; i < x*y; ++i) 42 | { 43 | byte r = data[i*4]; 44 | byte g = data[i*4 + 1]; 45 | byte b = data[i*4 + 2]; 46 | byte a = data[i*4 + 3]; 47 | 48 | 49 | data[i*4] = b; 50 | data[i*4 + 1] = g; 51 | data[i*4 + 2] = r; 52 | data[i*4 + 3] = a; 53 | } 54 | 55 | // Create Bitmap 56 | Bitmap bmp = new Bitmap(_loadedImage.Width, _loadedImage.Height, PixelFormat.Format32bppArgb); 57 | BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, _loadedImage.Width, _loadedImage.Height), ImageLockMode.WriteOnly, 58 | bmp.PixelFormat); 59 | 60 | Marshal.Copy(data, 0, bmpData.Scan0, bmpData.Stride*bmp.Height); 61 | bmp.UnlockBits(bmpData); 62 | ``` 63 | 64 | # Reliability & Performance 65 | There is special app to measure reliability & performance of SafeStbImageSharp in comparison to the original stb_image.h: https://github.com/StbSharp/SafeStbImageSharp/tree/master/tests/StbImageSharp.Testing 66 | 67 | It goes through every image file in the specified folder and tries to load it 10 times with SafeStbImageSharp, then 10 times with C++/CLI wrapper over the original stb_image.h(Stb.Native). Then it compares whether the results are byte-wise similar and also calculates loading times. Also it sums up and reports loading times for each method. 68 | 69 | Moreover SixLabor ImageSharp is included in the testing too. 70 | 71 | I've used it over following set of images: https://github.com/StbSharp/TestImages 72 | 73 | The byte-wise comprarison results are similar for StbImageSharp and Stb.Native. 74 | 75 | And performance comparison results are(times are total loading times): 76 | ``` 77 | 10 -- SafeStbImageSharp - jpg: 16139 ms, tga: 4075 ms, bmp: 370 ms, psd: 2 ms, png: 73274 ms, Total: 93860 ms 78 | 10 -- Stb.Native - jpg: 6437 ms, tga: 2140 ms, bmp: 132 ms, psd: 0 ms, png: 52758 ms, Total: 61467 ms 79 | 10 -- ImageSharp - jpg: 101309 ms, bmp: 63 ms, png: 44211 ms, Total: 145583 ms 80 | 10 -- Total files processed - jpg: 170, tga: 41, bmp: 7, psd: 1, png: 564, Total: 783 81 | 10 -- StbImageSharp/Stb.Native matches/processed - 783/787 82 | ``` 83 | 84 | # License 85 | Public Domain 86 | 87 | # Credits 88 | * [stb](https://github.com/nothings/stb) 89 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/SafeStbImageSharp.Samples.WinForms.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B} 8 | WinExe 9 | Properties 10 | StbImageLib.Samples.WinForms 11 | StbImageLib.Samples.WinForms 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Form 51 | 52 | 53 | Form1.cs 54 | 55 | 56 | 57 | 58 | Form1.cs 59 | 60 | 61 | ResXFileCodeGenerator 62 | Resources.Designer.cs 63 | Designer 64 | 65 | 66 | True 67 | Resources.resx 68 | True 69 | 70 | 71 | SettingsSingleFileGenerator 72 | Settings.Designer.cs 73 | 74 | 75 | True 76 | Settings.settings 77 | True 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | {750e8dc6-0af8-4772-8931-1623d0ea49cc} 86 | SafeStbImageSharp 87 | 88 | 89 | 90 | 97 | -------------------------------------------------------------------------------- /tests/Stb.Native/Stb.Native.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {EA7B8121-942A-437D-85D5-E4D55B94E88A} 15 | v4.5 16 | ManagedCProj 17 | StbNative 18 | 10.0 19 | 20 | 21 | 22 | DynamicLibrary 23 | true 24 | v142 25 | true 26 | Unicode 27 | 28 | 29 | DynamicLibrary 30 | false 31 | v142 32 | true 33 | Unicode 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | 48 | 49 | false 50 | 51 | 52 | 53 | Level3 54 | Disabled 55 | WIN32;_DEBUG;%(PreprocessorDefinitions) 56 | Use 57 | 58 | 59 | true 60 | 61 | 62 | 63 | 64 | 65 | Level3 66 | WIN32;NDEBUG;%(PreprocessorDefinitions) 67 | Use 68 | 69 | 70 | true 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Create 89 | Create 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /tests/Stb.Native/Stb.Native.h: -------------------------------------------------------------------------------- 1 | // Stb.Native.h 2 | 3 | #pragma once 4 | 5 | using namespace System; 6 | using namespace System::IO; 7 | using namespace System::Collections::Generic; 8 | using namespace System::Runtime::InteropServices; 9 | using namespace System::Threading; 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #define STBI_NO_STDIO 16 | #define STB_IMAGE_IMPLEMENTATION 17 | #include "stb_image.h" 18 | 19 | namespace StbNative { 20 | int read_callback(void *user, char *data, int size); 21 | void skip_callback(void *user, int size); 22 | int eof_callback(void *user); 23 | void write_func(void *context, void *data, int size); 24 | 25 | public ref class ReadInfo 26 | { 27 | public: 28 | Stream ^stream; 29 | array ^buffer; 30 | 31 | ReadInfo(Stream ^s, array ^b) 32 | { 33 | stream = s; 34 | buffer = b; 35 | } 36 | }; 37 | 38 | public ref class Native 39 | { 40 | public: 41 | static Dictionary ^readInfo = gcnew Dictionary(); 42 | static Dictionary ^writeInfo = gcnew Dictionary(); 43 | static int _id = 0; 44 | 45 | static int GenerateId() 46 | { 47 | int %trackRefCounter = _id; 48 | return System::Threading::Interlocked::Increment(trackRefCounter); 49 | } 50 | 51 | // TODO: Add your methods for this class here. 52 | static array ^ load_from_memory(array ^bytes, [Out] int %x, [Out] int %y, [Out] int %comp, int req_comp) 53 | { 54 | pin_ptr p = &bytes[0]; 55 | 56 | int xx, yy, ccomp; 57 | const unsigned char *ptr = (const unsigned char *)p; 58 | void *res = stbi_load_from_memory(ptr, bytes->Length, &xx, &yy, &ccomp, req_comp); 59 | 60 | x = xx; 61 | y = yy; 62 | comp = ccomp; 63 | 64 | int c = req_comp != 0 ? req_comp : comp; 65 | array ^result = gcnew array(x * y * c); 66 | 67 | Marshal::Copy(IntPtr((void *)res), result, 0, result->Length); 68 | free(res); 69 | 70 | return result; 71 | } 72 | 73 | static array ^ load_from_stream(Stream ^input, [Out] int %x, [Out] int %y, [Out] int %comp, int req_comp) 74 | { 75 | array ^buffer = gcnew array(32768); 76 | 77 | Monitor::Enter(readInfo); 78 | int id; 79 | try { 80 | id = GenerateId(); 81 | 82 | ReadInfo ^newInfo = gcnew ReadInfo(input, buffer); 83 | readInfo->Add(id, newInfo); 84 | } 85 | finally 86 | { 87 | Monitor::Exit(readInfo); 88 | } 89 | 90 | stbi_io_callbacks callbacks; 91 | callbacks.read = read_callback; 92 | callbacks.skip = skip_callback; 93 | callbacks.eof = eof_callback; 94 | 95 | int xx, yy, ccomp; 96 | 97 | void *res = stbi_load_from_callbacks(&callbacks, (void *)id, &xx, &yy, &ccomp, req_comp); 98 | 99 | x = xx; 100 | y = yy; 101 | comp = ccomp; 102 | 103 | int c = req_comp != 0 ? req_comp : comp; 104 | array ^result = gcnew array(x * y * c); 105 | 106 | Marshal::Copy(IntPtr((void *)res), result, 0, result->Length); 107 | free(res); 108 | 109 | buffer = nullptr; 110 | 111 | Monitor::Enter(readInfo); 112 | try { 113 | readInfo->Remove(id); 114 | } 115 | finally 116 | { 117 | Monitor::Exit(readInfo); 118 | } 119 | 120 | return result; 121 | } 122 | }; 123 | 124 | int read_callback(void *user, char *data, int size) 125 | { 126 | ReadInfo ^info; 127 | Monitor::Enter(Native::readInfo); 128 | int id = (int)user; 129 | try { 130 | info = Native::readInfo[id]; 131 | } 132 | finally 133 | { 134 | Monitor::Exit(Native::readInfo); 135 | } 136 | 137 | if (size > info->buffer->Length) { 138 | info->buffer = gcnew array(size * 2); 139 | } 140 | 141 | int res = info->stream->Read(info->buffer, 0, size); 142 | 143 | Marshal::Copy(info->buffer, 0, IntPtr(data), res); 144 | 145 | return res; 146 | } 147 | 148 | void skip_callback(void *user, int size) 149 | { 150 | ReadInfo ^info; 151 | Monitor::Enter(Native::readInfo); 152 | int id = (int)user; 153 | try { 154 | info = Native::readInfo[id]; 155 | } 156 | finally 157 | { 158 | Monitor::Exit(Native::readInfo); 159 | } 160 | 161 | info->stream->Seek(size, SeekOrigin::Current); 162 | } 163 | 164 | int eof_callback(void *user) 165 | { 166 | ReadInfo ^info; 167 | Monitor::Enter(Native::readInfo); 168 | int id = (int)user; 169 | try { 170 | info = Native::readInfo[id]; 171 | } 172 | finally 173 | { 174 | Monitor::Exit(Native::readInfo); 175 | } 176 | 177 | return info->stream->CanRead ? 1 : 0; 178 | } 179 | 180 | void write_func(void *context, void *data, int size) 181 | { 182 | Stream ^ info; 183 | Monitor::Enter(Native::writeInfo); 184 | int id = (int)context; 185 | try { 186 | info = Native::writeInfo[id]; 187 | } 188 | finally 189 | { 190 | Monitor::Exit(Native::writeInfo); 191 | } 192 | 193 | unsigned char *bptr = (unsigned char *)data; 194 | for (int i = 0; i < size; ++i) 195 | { 196 | info->WriteByte(*bptr); 197 | ++bptr; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace StbSharp.WinForms.Test 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.button1 = new System.Windows.Forms.Button(); 32 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 33 | this._numericWidth = new System.Windows.Forms.NumericUpDown(); 34 | this.label1 = new System.Windows.Forms.Label(); 35 | this.label2 = new System.Windows.Forms.Label(); 36 | this._numericHeight = new System.Windows.Forms.NumericUpDown(); 37 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 38 | ((System.ComponentModel.ISupportInitialize)(this._numericWidth)).BeginInit(); 39 | ((System.ComponentModel.ISupportInitialize)(this._numericHeight)).BeginInit(); 40 | this.SuspendLayout(); 41 | // 42 | // button1 43 | // 44 | this.button1.Location = new System.Drawing.Point(13, 13); 45 | this.button1.Name = "button1"; 46 | this.button1.Size = new System.Drawing.Size(75, 23); 47 | this.button1.TabIndex = 0; 48 | this.button1.Text = "Load..."; 49 | this.button1.UseVisualStyleBackColor = true; 50 | this.button1.Click += new System.EventHandler(this.button1_Click); 51 | // 52 | // pictureBox1 53 | // 54 | this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 55 | | System.Windows.Forms.AnchorStyles.Left) 56 | | System.Windows.Forms.AnchorStyles.Right))); 57 | this.pictureBox1.Location = new System.Drawing.Point(12, 42); 58 | this.pictureBox1.Name = "pictureBox1"; 59 | this.pictureBox1.Size = new System.Drawing.Size(707, 491); 60 | this.pictureBox1.TabIndex = 1; 61 | this.pictureBox1.TabStop = false; 62 | // 63 | // _numericWidth 64 | // 65 | this._numericWidth.Location = new System.Drawing.Point(429, 13); 66 | this._numericWidth.Maximum = new decimal(new int[] { 67 | 10000, 68 | 0, 69 | 0, 70 | 0}); 71 | this._numericWidth.Name = "_numericWidth"; 72 | this._numericWidth.Size = new System.Drawing.Size(120, 20); 73 | this._numericWidth.TabIndex = 7; 74 | // 75 | // label1 76 | // 77 | this.label1.AutoSize = true; 78 | this.label1.Location = new System.Drawing.Point(385, 15); 79 | this.label1.Name = "label1"; 80 | this.label1.Size = new System.Drawing.Size(38, 13); 81 | this.label1.TabIndex = 8; 82 | this.label1.Text = "Width:"; 83 | // 84 | // label2 85 | // 86 | this.label2.AutoSize = true; 87 | this.label2.Location = new System.Drawing.Point(555, 15); 88 | this.label2.Name = "label2"; 89 | this.label2.Size = new System.Drawing.Size(41, 13); 90 | this.label2.TabIndex = 10; 91 | this.label2.Text = "Height:"; 92 | // 93 | // _numericHeight 94 | // 95 | this._numericHeight.Location = new System.Drawing.Point(599, 13); 96 | this._numericHeight.Maximum = new decimal(new int[] { 97 | 10000, 98 | 0, 99 | 0, 100 | 0}); 101 | this._numericHeight.Name = "_numericHeight"; 102 | this._numericHeight.Size = new System.Drawing.Size(120, 20); 103 | this._numericHeight.TabIndex = 9; 104 | // 105 | // Form1 106 | // 107 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 108 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 109 | this.ClientSize = new System.Drawing.Size(731, 545); 110 | this.Controls.Add(this.label2); 111 | this.Controls.Add(this._numericHeight); 112 | this.Controls.Add(this.label1); 113 | this.Controls.Add(this._numericWidth); 114 | this.Controls.Add(this.pictureBox1); 115 | this.Controls.Add(this.button1); 116 | this.Name = "Form1"; 117 | this.Text = "Form1"; 118 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 119 | ((System.ComponentModel.ISupportInitialize)(this._numericWidth)).EndInit(); 120 | ((System.ComponentModel.ISupportInitialize)(this._numericHeight)).EndInit(); 121 | this.ResumeLayout(false); 122 | this.PerformLayout(); 123 | 124 | } 125 | 126 | #endregion 127 | 128 | private System.Windows.Forms.Button button1; 129 | private System.Windows.Forms.PictureBox pictureBox1; 130 | private System.Windows.Forms.NumericUpDown _numericWidth; 131 | private System.Windows.Forms.Label label1; 132 | private System.Windows.Forms.Label label2; 133 | private System.Windows.Forms.NumericUpDown _numericHeight; 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /tests/StbImageSharp.Testing/StbImageSharp.Testing.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6} 9 | Exe 10 | StbImageSharp.Testing 11 | StbImageSharp.Testing 12 | v4.7.2 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | ..\..\packages\SixLabors.Core.1.0.0-beta0008\lib\netstandard2.0\SixLabors.Core.dll 41 | 42 | 43 | ..\..\packages\SixLabors.ImageSharp.1.0.0-beta0007\lib\net472\SixLabors.ImageSharp.dll 44 | 45 | 46 | 47 | ..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll 48 | 49 | 50 | 51 | ..\..\packages\System.Memory.4.5.1\lib\netstandard2.0\System.Memory.dll 52 | 53 | 54 | 55 | ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 56 | 57 | 58 | 59 | ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.1\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {750e8dc6-0af8-4772-8931-1623d0ea49cc} 77 | SafeStbImageSharp 78 | 79 | 80 | {ea7b8121-942a-437d-85d5-e4d55b94e88a} 81 | Stb.Native 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Form1.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /samples/SafeStbImageSharp.Samples.WinForms/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /SafeStbImageSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeStbImageSharp", "src\SafeStbImageSharp.csproj", "{750E8DC6-0AF8-4772-8931-1623D0EA49CC}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{56C50AF4-B951-44C2-962F-89DB441ACEBD}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SafeStbImageSharp.Samples.MonoGame", "samples\SafeStbImageSharp.Samples.MonoGame\SafeStbImageSharp.Samples.MonoGame.csproj", "{4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeStbImageSharp.Samples.WinForms", "samples\SafeStbImageSharp.Samples.WinForms\SafeStbImageSharp.Samples.WinForms.csproj", "{87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{4DF85107-02EF-46F2-A399-D69461537899}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Stb.Native", "tests\Stb.Native\Stb.Native.vcxproj", "{EA7B8121-942A-437D-85D5-E4D55B94E88A}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeStbImageSharp.Tests", "tests\SafeStbImageSharp.Tests\SafeStbImageSharp.Tests.csproj", "{EC85117B-2ABE-43BD-950F-D26C00669F12}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StbImageSharp.Testing", "tests\StbImageSharp.Testing\StbImageSharp.Testing.csproj", "{4295DB99-F5C5-4D33-84DA-02AA2CA141B6}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Debug|x86 = Debug|x86 26 | Release|Any CPU = Release|Any CPU 27 | Release|x86 = Release|x86 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Debug|x86.ActiveCfg = Debug|Any CPU 33 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Debug|x86.Build.0 = Debug|Any CPU 34 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Release|x86.ActiveCfg = Release|Any CPU 37 | {750E8DC6-0AF8-4772-8931-1623D0EA49CC}.Release|x86.Build.0 = Release|Any CPU 38 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Debug|x86.Build.0 = Debug|Any CPU 42 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Release|x86.ActiveCfg = Release|Any CPU 45 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF}.Release|x86.Build.0 = Release|Any CPU 46 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Debug|x86.ActiveCfg = Debug|Any CPU 49 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Debug|x86.Build.0 = Debug|Any CPU 50 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Release|x86.ActiveCfg = Release|Any CPU 53 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B}.Release|x86.Build.0 = Release|Any CPU 54 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Debug|Any CPU.ActiveCfg = Debug|Win32 55 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Debug|Any CPU.Build.0 = Debug|Win32 56 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Debug|x86.ActiveCfg = Debug|Win32 57 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Debug|x86.Build.0 = Debug|Win32 58 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Release|Any CPU.ActiveCfg = Release|Win32 59 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Release|Any CPU.Build.0 = Release|Win32 60 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Release|x86.ActiveCfg = Release|Win32 61 | {EA7B8121-942A-437D-85D5-E4D55B94E88A}.Release|x86.Build.0 = Release|Win32 62 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Debug|x86.ActiveCfg = Debug|Any CPU 65 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Debug|x86.Build.0 = Debug|Any CPU 66 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Release|x86.ActiveCfg = Release|Any CPU 69 | {EC85117B-2ABE-43BD-950F-D26C00669F12}.Release|x86.Build.0 = Release|Any CPU 70 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Debug|x86.ActiveCfg = Debug|Any CPU 73 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Debug|x86.Build.0 = Debug|Any CPU 74 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Release|x86.ActiveCfg = Release|Any CPU 77 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6}.Release|x86.Build.0 = Release|Any CPU 78 | EndGlobalSection 79 | GlobalSection(SolutionProperties) = preSolution 80 | HideSolutionNode = FALSE 81 | EndGlobalSection 82 | GlobalSection(NestedProjects) = preSolution 83 | {4E1D18DA-4594-408F-AC3A-2C3D7357C9DF} = {56C50AF4-B951-44C2-962F-89DB441ACEBD} 84 | {87A1E8BA-4F87-4BE7-84DD-BECD47E9944B} = {56C50AF4-B951-44C2-962F-89DB441ACEBD} 85 | {EA7B8121-942A-437D-85D5-E4D55B94E88A} = {4DF85107-02EF-46F2-A399-D69461537899} 86 | {EC85117B-2ABE-43BD-950F-D26C00669F12} = {4DF85107-02EF-46F2-A399-D69461537899} 87 | {4295DB99-F5C5-4D33-84DA-02AA2CA141B6} = {4DF85107-02EF-46F2-A399-D69461537899} 88 | EndGlobalSection 89 | GlobalSection(ExtensibilityGlobals) = postSolution 90 | SolutionGuid = {C9C812E7-F89A-4B6E-8F9D-981D5F8BBAB3} 91 | EndGlobalSection 92 | EndGlobal 93 | -------------------------------------------------------------------------------- /src/Decoding/PsdDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using StbImageSharp.Utility; 4 | 5 | namespace StbImageSharp.Decoding 6 | { 7 | #if !STBSHARP_INTERNAL 8 | public 9 | #else 10 | internal 11 | #endif 12 | class PsdDecoder : Decoder 13 | { 14 | private PsdDecoder(Stream stream) : base(stream) 15 | { 16 | } 17 | 18 | private int stbi__psd_decode_rle(FakePtr p, int pixelCount) 19 | { 20 | var count = 0; 21 | var nleft = 0; 22 | var len = 0; 23 | count = 0; 24 | while ((nleft = pixelCount - count) > 0) 25 | { 26 | len = stbi__get8(); 27 | if (len == 128) 28 | { 29 | } 30 | else if (len < 128) 31 | { 32 | len++; 33 | if (len > nleft) 34 | return 0; 35 | count += len; 36 | while (len != 0) 37 | { 38 | p.Value = stbi__get8(); 39 | p += 4; 40 | len--; 41 | } 42 | } 43 | else if (len > 128) 44 | { 45 | byte val = 0; 46 | len = 257 - len; 47 | if (len > nleft) 48 | return 0; 49 | val = stbi__get8(); 50 | count += len; 51 | while (len != 0) 52 | { 53 | p.Value = val; 54 | p += 4; 55 | len--; 56 | } 57 | } 58 | } 59 | 60 | return 1; 61 | } 62 | 63 | private ImageResult InternalDecode(ColorComponents? requiredComponents, int bpc) 64 | { 65 | var pixelCount = 0; 66 | var channelCount = 0; 67 | var compression = 0; 68 | var channel = 0; 69 | var i = 0; 70 | var bitdepth = 0; 71 | var w = 0; 72 | var h = 0; 73 | byte[] _out_; 74 | if (stbi__get32be() != 0x38425053) 75 | stbi__err("not PSD"); 76 | if (stbi__get16be() != 1) 77 | stbi__err("wrong version"); 78 | stbi__skip(6); 79 | channelCount = stbi__get16be(); 80 | if (channelCount < 0 || channelCount > 16) 81 | stbi__err("wrong channel count"); 82 | h = (int)stbi__get32be(); 83 | w = (int)stbi__get32be(); 84 | bitdepth = stbi__get16be(); 85 | if (bitdepth != 8 && bitdepth != 16) 86 | stbi__err("unsupported bit depth"); 87 | if (stbi__get16be() != 3) 88 | stbi__err("wrong color format"); 89 | stbi__skip((int)stbi__get32be()); 90 | stbi__skip((int)stbi__get32be()); 91 | stbi__skip((int)stbi__get32be()); 92 | compression = stbi__get16be(); 93 | if (compression > 1) 94 | stbi__err("bad compression"); 95 | 96 | var bits_per_channel = 8; 97 | if (compression == 0 && bitdepth == 16 && bpc == 16) 98 | { 99 | _out_ = new byte[8 * w * h]; 100 | bits_per_channel = 16; 101 | } 102 | else 103 | { 104 | _out_ = new byte[4 * w * h]; 105 | } 106 | 107 | pixelCount = w * h; 108 | 109 | var ptr = new FakePtr(_out_); 110 | if (compression != 0) 111 | { 112 | stbi__skip(h * channelCount * 2); 113 | for (channel = 0; channel < 4; channel++) 114 | { 115 | FakePtr p; 116 | p = ptr + channel; 117 | if (channel >= channelCount) 118 | { 119 | for (i = 0; i < pixelCount; i++, p += 4) p.Set((byte)(channel == 3 ? 255 : 0)); 120 | } 121 | else 122 | { 123 | if (stbi__psd_decode_rle(p, pixelCount) == 0) stbi__err("corrupt"); 124 | } 125 | } 126 | } 127 | else 128 | { 129 | for (channel = 0; channel < 4; channel++) 130 | if (channel >= channelCount) 131 | { 132 | if (bitdepth == 16 && bpc == 16) 133 | throw new NotImplementedException(); 134 | /* ushort* q = ((ushort*)(ptr)) + channel; 135 | ushort val = (ushort)((channel) == (3) ? 65535 : 0); 136 | for (i = (int)(0); (i) < (pixelCount); i++, q += 4) 137 | { 138 | *q = (ushort)(val); 139 | }*/ 140 | 141 | var p = ptr + channel; 142 | var val = (byte)(channel == 3 ? 255 : 0); 143 | for (i = 0; i < pixelCount; i++, p += 4) p.Set(val); 144 | } 145 | else 146 | { 147 | if (bits_per_channel == 16) 148 | throw new NotImplementedException(); 149 | /* ushort* q = ((ushort*)(ptr)) + channel; 150 | for (i = (int)(0); (i) < (pixelCount); i++, q += 4) 151 | { 152 | *q = ((ushort)(stbi__get16be())); 153 | }*/ 154 | 155 | var p = ptr + channel; 156 | if (bitdepth == 16) 157 | for (i = 0; i < pixelCount; i++, p += 4) 158 | p.Set((byte)(stbi__get16be() >> 8)); 159 | else 160 | for (i = 0; i < pixelCount; i++, p += 4) 161 | p.Set(stbi__get8()); 162 | } 163 | } 164 | 165 | if (channelCount >= 4) 166 | { 167 | if (bits_per_channel == 16) 168 | throw new NotImplementedException(); 169 | /* for (i = (int)(0); (i) < (w * h); ++i) 170 | { 171 | ushort* pixel = (ushort*)(ptr) + 4 * i; 172 | if ((pixel[3] != 0) && (pixel[3] != 65535)) 173 | { 174 | float a = (float)(pixel[3] / 65535.0f); 175 | float ra = (float)(1.0f / a); 176 | float inv_a = (float)(65535.0f * (1 - ra)); 177 | pixel[0] = ((ushort)(pixel[0] * ra + inv_a)); 178 | pixel[1] = ((ushort)(pixel[1] * ra + inv_a)); 179 | pixel[2] = ((ushort)(pixel[2] * ra + inv_a)); 180 | } 181 | }*/ 182 | for (i = 0; i < w * h; ++i) 183 | { 184 | var pixel = ptr + 4 * i; 185 | if (pixel[3] != 0 && pixel[3] != 255) 186 | { 187 | var a = pixel[3] / 255.0f; 188 | var ra = 1.0f / a; 189 | var inv_a = 255.0f * (1 - ra); 190 | pixel[0] = (byte)(pixel[0] * ra + inv_a); 191 | pixel[1] = (byte)(pixel[1] * ra + inv_a); 192 | pixel[2] = (byte)(pixel[2] * ra + inv_a); 193 | } 194 | } 195 | } 196 | 197 | var req_comp = requiredComponents.ToReqComp(); 198 | if (req_comp != 0 && req_comp != 4) 199 | { 200 | if (bits_per_channel == 16) 201 | _out_ = Conversion.stbi__convert_format16(_out_, 4, req_comp, (uint)w, (uint)h); 202 | else 203 | _out_ = Conversion.stbi__convert_format(_out_, 4, req_comp, (uint)w, (uint)h); 204 | } 205 | 206 | return new ImageResult 207 | { 208 | Width = w, 209 | Height = h, 210 | SourceComponents = ColorComponents.RedGreenBlueAlpha, 211 | ColorComponents = requiredComponents != null 212 | ? requiredComponents.Value 213 | : ColorComponents.RedGreenBlueAlpha, 214 | BitsPerChannel = bits_per_channel, 215 | Data = _out_ 216 | }; 217 | } 218 | 219 | public static bool Test(Stream stream) 220 | { 221 | var r = stream.stbi__get32be() == 0x38425053; 222 | stream.Rewind(); 223 | 224 | return r; 225 | } 226 | 227 | public static ImageInfo? Info(Stream stream) 228 | { 229 | try 230 | { 231 | if (stream.stbi__get32be() != 0x38425053) return null; 232 | 233 | if (stream.stbi__get16be() != 1) return null; 234 | 235 | stream.stbi__skip(6); 236 | var channelCount = stream.stbi__get16be(); 237 | if (channelCount < 0 || channelCount > 16) return null; 238 | 239 | var height = (int)stream.stbi__get32be(); 240 | var width = (int)stream.stbi__get32be(); 241 | var depth = stream.stbi__get16be(); 242 | if (depth != 8 && depth != 16) return null; 243 | 244 | if (stream.stbi__get16be() != 3) return null; 245 | 246 | return new ImageInfo 247 | { 248 | Width = width, 249 | Height = height, 250 | ColorComponents = ColorComponents.RedGreenBlueAlpha, 251 | BitsPerChannel = depth 252 | }; 253 | } 254 | finally 255 | { 256 | stream.Rewind(); 257 | } 258 | } 259 | 260 | public static ImageResult Decode(Stream stream, ColorComponents? requiredComponents = null, int bpc = 8) 261 | { 262 | var decoder = new PsdDecoder(stream); 263 | return decoder.InternalDecode(requiredComponents, bpc); 264 | } 265 | } 266 | } -------------------------------------------------------------------------------- /tests/StbImageSharp.Testing/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using SixLabors.ImageSharp; 11 | using SixLabors.ImageSharp.Advanced; 12 | using SixLabors.ImageSharp.PixelFormats; 13 | using StbNative; 14 | 15 | namespace StbImageSharp.Testing 16 | { 17 | internal static class Program 18 | { 19 | private class LoadResult 20 | { 21 | public int Width; 22 | public int Height; 23 | public ColorComponents? Components; 24 | public byte[] Data; 25 | public int TimeInMs; 26 | } 27 | 28 | private class LoadingTimes 29 | { 30 | private readonly ConcurrentDictionary _byExtension = new ConcurrentDictionary(); 31 | private readonly ConcurrentDictionary _byExtensionCount = new ConcurrentDictionary(); 32 | private int _total, _totalCount; 33 | 34 | public void Add(string extension, int value) 35 | { 36 | if (!_byExtension.ContainsKey(extension)) 37 | { 38 | _byExtension[extension] = 0; 39 | _byExtensionCount[extension] = 0; 40 | } 41 | 42 | _byExtension[extension] += value; 43 | ++_byExtensionCount[extension]; 44 | _total += value; 45 | ++_totalCount; 46 | } 47 | 48 | public string BuildString() 49 | { 50 | var sb = new StringBuilder(); 51 | foreach (var pair in _byExtension) 52 | { 53 | sb.AppendFormat("{0}: {1} ms, ", pair.Key, pair.Value); 54 | } 55 | 56 | sb.AppendFormat("Total: {0} ms", _total); 57 | 58 | return sb.ToString(); 59 | } 60 | 61 | public string BuildStringCount() 62 | { 63 | var sb = new StringBuilder(); 64 | foreach (var pair in _byExtensionCount) 65 | { 66 | sb.AppendFormat("{0}: {1}, ", pair.Key, pair.Value); 67 | } 68 | 69 | sb.AppendFormat("Total: {0}", _totalCount); 70 | 71 | return sb.ToString(); 72 | } 73 | } 74 | 75 | private const int LoadTries = 10; 76 | private static int tasksStarted; 77 | private static int filesProcessed, filesMatches; 78 | private static LoadingTimes stbImageSharpTotal = new LoadingTimes(); 79 | private static LoadingTimes stbNativeTotal = new LoadingTimes(); 80 | private static LoadingTimes imageSharpTotal = new LoadingTimes(); 81 | 82 | public static void Log(string message) 83 | { 84 | Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " -- " + message); 85 | } 86 | 87 | public static void Log(string format, params object[] args) 88 | { 89 | Log(string.Format(format, args)); 90 | } 91 | 92 | private static void BeginWatch(Stopwatch sw) 93 | { 94 | sw.Restart(); 95 | } 96 | 97 | private static int EndWatch(Stopwatch sw) 98 | { 99 | sw.Stop(); 100 | return (int)sw.ElapsedMilliseconds; 101 | } 102 | 103 | private static LoadResult ParseTest(string name, LoadDelegate load) 104 | { 105 | var sw = new Stopwatch(); 106 | 107 | Log("With " + name); 108 | int x = 0, y = 0; 109 | ColorComponents? comp = null; 110 | var parsed = new byte[0]; 111 | BeginWatch(sw); 112 | 113 | for (var i = 0; i < LoadTries; ++i) 114 | parsed = load(out x, out y, out comp); 115 | 116 | Log("x: {0}, y: {1}, comp: {2}, size: {3}", x, y, comp != null ? comp.Value.ToString() : "default", parsed.Length); 117 | var passed = EndWatch(sw) / LoadTries; 118 | Log("Span: {0} ms", passed); 119 | 120 | return new LoadResult 121 | { 122 | Width = x, 123 | Height = y, 124 | Components = comp, 125 | Data = parsed, 126 | TimeInMs = passed 127 | }; 128 | } 129 | 130 | public static bool RunTests(string imagesPath) 131 | { 132 | var files = Directory.EnumerateFiles(imagesPath, "*.*", SearchOption.AllDirectories).ToArray(); 133 | 134 | Log("Files count: {0}", files.Length); 135 | 136 | foreach (var file in files) 137 | { 138 | Task.Factory.StartNew(() => { ThreadProc(file); }); 139 | Interlocked.Increment(ref tasksStarted); 140 | } 141 | 142 | while (true) 143 | { 144 | Thread.Sleep(1000); 145 | 146 | if (tasksStarted == 0) 147 | break; 148 | } 149 | 150 | return true; 151 | } 152 | 153 | private static void ThreadProc(string f) 154 | { 155 | if (!f.EndsWith(".bmp") && !f.EndsWith(".jpg") && !f.EndsWith(".png") && 156 | !f.EndsWith(".jpg") && !f.EndsWith(".psd") && !f.EndsWith(".pic") && 157 | !f.EndsWith(".tga")) 158 | { 159 | Interlocked.Decrement(ref tasksStarted); 160 | return; 161 | } 162 | 163 | bool match = false; 164 | try 165 | { 166 | Log(string.Empty); 167 | Log("{0}: Loading {1} into memory", DateTime.Now.ToLongTimeString(), f); 168 | var data = File.ReadAllBytes(f); 169 | var extension = Path.GetExtension(f).ToLower(); 170 | if (extension.StartsWith(".")) 171 | { 172 | extension = extension.Substring(1); 173 | } 174 | 175 | Log("----------------------------"); 176 | 177 | var stbImageSharpResult = ParseTest( 178 | "StbImageSharp", 179 | (out int x, out int y, out ColorComponents? ccomp) => 180 | { 181 | var img = ImageResult.FromMemory(data, ColorComponents.RedGreenBlueAlpha); 182 | 183 | x = img.Width; 184 | y = img.Height; 185 | ccomp = img.SourceComponents; 186 | 187 | return img.Data; 188 | }); 189 | 190 | var stbNativeResult = ParseTest( 191 | "Stb.Native", 192 | (out int x, out int y, out ColorComponents? ccomp) => 193 | { 194 | var result = Native.load_from_memory(data, out x, out y, out var icomp, 195 | (int)ColorComponents.RedGreenBlueAlpha); 196 | ccomp = (ColorComponents)icomp; 197 | return result; 198 | }); 199 | 200 | 201 | if (stbImageSharpResult.Width != stbNativeResult.Width) 202 | throw new Exception(string.Format("Inconsistent x: StbSharp={0}, Stb.Native={1}", stbImageSharpResult.Width, stbNativeResult.Width)); 203 | 204 | if (stbImageSharpResult.Height != stbNativeResult.Height) 205 | throw new Exception(string.Format("Inconsistent y: StbSharp={0}, Stb.Native={1}", stbImageSharpResult.Height, stbNativeResult.Height)); 206 | 207 | if (stbImageSharpResult.Components != stbNativeResult.Components) 208 | throw new Exception(string.Format("Inconsistent comp: StbSharp={0}, Stb.Native={1}", stbImageSharpResult.Components, stbNativeResult.Components)); 209 | 210 | if (stbImageSharpResult.Data.Length != stbNativeResult.Data.Length) 211 | throw new Exception(string.Format("Inconsistent parsed length: StbSharp={0}, Stb.Native={1}", 212 | stbImageSharpResult.Data.Length, 213 | stbNativeResult.Data.Length)); 214 | 215 | for (var i = 0; i < stbImageSharpResult.Data.Length; ++i) 216 | if (stbImageSharpResult.Data[i] != stbNativeResult.Data[i]) 217 | throw new Exception(string.Format("Inconsistent data: index={0}, StbSharp={1}, Stb.Native={2}", 218 | i, 219 | (int)stbImageSharpResult.Data[i], 220 | (int)stbNativeResult.Data[i])); 221 | 222 | match = true; 223 | 224 | if (extension != "tga" && extension != "psd" && extension != "pic") 225 | { 226 | var imageSharpResult = ParseTest( 227 | "ImageSharp", 228 | (out int x, out int y, out ColorComponents? ccomp) => 229 | { 230 | using (Image image = Image.Load(data)) 231 | { 232 | x = image.Width; 233 | y = image.Height; 234 | ccomp = null; 235 | 236 | return MemoryMarshal.AsBytes(image.GetPixelSpan()).ToArray(); 237 | } 238 | } 239 | ); 240 | imageSharpTotal.Add(extension, imageSharpResult.TimeInMs); 241 | } 242 | 243 | stbImageSharpTotal.Add(extension, stbImageSharpResult.TimeInMs); 244 | stbNativeTotal.Add(extension, stbNativeResult.TimeInMs); 245 | } 246 | catch (Exception ex) 247 | { 248 | Log("Error: " + ex.Message); 249 | } 250 | finally 251 | { 252 | if (match) 253 | { 254 | Interlocked.Increment(ref filesMatches); 255 | } 256 | 257 | Interlocked.Increment(ref filesProcessed); 258 | Interlocked.Decrement(ref tasksStarted); 259 | 260 | Log("StbImageSharp - {0}", stbImageSharpTotal.BuildString()); 261 | Log("Stb.Native - {0}", stbNativeTotal.BuildString()); 262 | Log("ImageSharp - {0}", imageSharpTotal.BuildString()); 263 | Log("Total files processed - {0}", stbImageSharpTotal.BuildStringCount()); 264 | Log("StbImageSharp/Stb.Native matches/processed - {0}/{1}", filesMatches, filesProcessed); 265 | Log("Tasks left - {0}", tasksStarted); 266 | Log("GC Memory - {0}", GC.GetTotalMemory(true)); 267 | } 268 | } 269 | 270 | public static int Main(string[] args) 271 | { 272 | try 273 | { 274 | if (args == null || args.Length < 1) 275 | { 276 | Console.WriteLine("Usage: StbImageSharp.Testing "); 277 | return 1; 278 | } 279 | 280 | var start = DateTime.Now; 281 | 282 | var res = RunTests(args[0]); 283 | var passed = DateTime.Now - start; 284 | Log("Span: {0} ms", passed.TotalMilliseconds); 285 | Log(DateTime.Now.ToLongTimeString() + " -- " + (res ? "Success" : "Failure")); 286 | 287 | return res ? 1 : 0; 288 | } 289 | catch (Exception ex) 290 | { 291 | Console.WriteLine(ex); 292 | return 0; 293 | } 294 | } 295 | 296 | private delegate void WriteDelegate(ImageResult image, Stream stream); 297 | 298 | private delegate byte[] LoadDelegate(out int x, out int y, out ColorComponents? comp); 299 | } 300 | } -------------------------------------------------------------------------------- /src/Utility/Conversion.cs: -------------------------------------------------------------------------------- 1 | using StbImageSharp.Decoding; 2 | using StbImageSharp.Utility; 3 | using System; 4 | 5 | namespace StbImageSharp 6 | { 7 | internal static class Conversion 8 | { 9 | public static byte stbi__compute_y(int r, int g, int b) 10 | { 11 | return (byte)(((r * 77) + (g * 150) + (29 * b)) >> 8); 12 | } 13 | 14 | public static ushort stbi__compute_y_16(int r, int g, int b) 15 | { 16 | return (ushort)(((r * 77) + (g * 150) + (29 * b)) >> 8); 17 | } 18 | 19 | public static byte[] stbi__convert_format16(byte[] data, int img_n, int req_comp, uint x, uint y) 20 | { 21 | throw new NotImplementedException(); 22 | /* int i = 0; 23 | int j = 0; 24 | if ((req_comp) == (img_n)) 25 | return data; 26 | 27 | var good = new byte[req_comp * x * y * 2]; 28 | FakePtr dataPtr = new FakePtr(data); 29 | FakePtr goodPtr = new FakePtr(good); 30 | for (j = (int)(0); (j) < ((int)(y)); ++j) 31 | { 32 | ushort* src = (ushort*)dataPtr + j * x * img_n; 33 | ushort* dest = (ushort*)goodPtr + j * x * req_comp; 34 | switch (((img_n) * 8 + (req_comp))) 35 | { 36 | case ((1) * 8 + (2)): 37 | for (i = (int)(x - 1); (i) >= (0); --i, src += 1, dest += 2) 38 | { 39 | dest[0] = (ushort)(src[0]); 40 | dest[1] = (ushort)(0xffff); 41 | } 42 | break; 43 | case ((1) * 8 + (3)): 44 | for (i = (int)(x - 1); (i) >= (0); --i, src += 1, dest += 3) 45 | { 46 | dest[0] = (ushort)(dest[1] = (ushort)(dest[2] = (ushort)(src[0]))); 47 | } 48 | break; 49 | case ((1) * 8 + (4)): 50 | for (i = (int)(x - 1); (i) >= (0); --i, src += 1, dest += 4) 51 | { 52 | dest[0] = (ushort)(dest[1] = (ushort)(dest[2] = (ushort)(src[0]))); 53 | dest[3] = (ushort)(0xffff); 54 | } 55 | break; 56 | case ((2) * 8 + (1)): 57 | for (i = (int)(x - 1); (i) >= (0); --i, src += 2, dest += 1) 58 | { 59 | dest[0] = (ushort)(src[0]); 60 | } 61 | break; 62 | case ((2) * 8 + (3)): 63 | for (i = (int)(x - 1); (i) >= (0); --i, src += 2, dest += 3) 64 | { 65 | dest[0] = (ushort)(dest[1] = (ushort)(dest[2] = (ushort)(src[0]))); 66 | } 67 | break; 68 | case ((2) * 8 + (4)): 69 | for (i = (int)(x - 1); (i) >= (0); --i, src += 2, dest += 4) 70 | { 71 | dest[0] = (ushort)(dest[1] = (ushort)(dest[2] = (ushort)(src[0]))); 72 | dest[3] = (ushort)(src[1]); 73 | } 74 | break; 75 | case ((3) * 8 + (4)): 76 | for (i = (int)(x - 1); (i) >= (0); --i, src += 3, dest += 4) 77 | { 78 | dest[0] = (ushort)(src[0]); 79 | dest[1] = (ushort)(src[1]); 80 | dest[2] = (ushort)(src[2]); 81 | dest[3] = (ushort)(0xffff); 82 | } 83 | break; 84 | case ((3) * 8 + (1)): 85 | for (i = (int)(x - 1); (i) >= (0); --i, src += 3, dest += 1) 86 | { 87 | dest[0] = (ushort)(stbi__compute_y_16((int)(src[0]), (int)(src[1]), (int)(src[2]))); 88 | } 89 | break; 90 | case ((3) * 8 + (2)): 91 | for (i = (int)(x - 1); (i) >= (0); --i, src += 3, dest += 2) 92 | { 93 | dest[0] = (ushort)(stbi__compute_y_16((int)(src[0]), (int)(src[1]), (int)(src[2]))); 94 | dest[1] = (ushort)(0xffff); 95 | } 96 | break; 97 | case ((4) * 8 + (1)): 98 | for (i = (int)(x - 1); (i) >= (0); --i, src += 4, dest += 1) 99 | { 100 | dest[0] = (ushort)(stbi__compute_y_16((int)(src[0]), (int)(src[1]), (int)(src[2]))); 101 | } 102 | break; 103 | case ((4) * 8 + (2)): 104 | for (i = (int)(x - 1); (i) >= (0); --i, src += 4, dest += 2) 105 | { 106 | dest[0] = (ushort)(stbi__compute_y_16((int)(src[0]), (int)(src[1]), (int)(src[2]))); 107 | dest[1] = (ushort)(src[3]); 108 | } 109 | break; 110 | case ((4) * 8 + (3)): 111 | for (i = (int)(x - 1); (i) >= (0); --i, src += 4, dest += 3) 112 | { 113 | dest[0] = (ushort)(src[0]); 114 | dest[1] = (ushort)(src[1]); 115 | dest[2] = (ushort)(src[2]); 116 | } 117 | break; 118 | default: 119 | Decoder.stbi__err("0"); 120 | break; 121 | } 122 | } 123 | 124 | return good;*/ 125 | } 126 | 127 | public static byte[] stbi__convert_format(byte[] data, int img_n, int req_comp, uint x, uint y) 128 | { 129 | int i = 0; 130 | int j = 0; 131 | if ((req_comp) == (img_n)) 132 | return data; 133 | 134 | var good = new byte[req_comp * x * y]; 135 | for (j = (int)(0); (j) < ((int)(y)); ++j) 136 | { 137 | FakePtr src = new FakePtr(data, (int)(j * x * img_n)); 138 | FakePtr dest = new FakePtr(good, (int)(j * x * req_comp)); 139 | switch (((img_n) * 8 + (req_comp))) 140 | { 141 | case ((1) * 8 + (2)): 142 | for (i = (int)(x - 1); (i) >= (0); --i, src += 1, dest += 2) 143 | { 144 | dest[0] = (byte)(src[0]); 145 | dest[1] = (byte)(255); 146 | } 147 | break; 148 | case ((1) * 8 + (3)): 149 | for (i = (int)(x - 1); (i) >= (0); --i, src += 1, dest += 3) 150 | { 151 | dest[0] = (byte)(dest[1] = (byte)(dest[2] = (byte)(src[0]))); 152 | } 153 | break; 154 | case ((1) * 8 + (4)): 155 | for (i = (int)(x - 1); (i) >= (0); --i, src += 1, dest += 4) 156 | { 157 | dest[0] = (byte)(dest[1] = (byte)(dest[2] = (byte)(src[0]))); 158 | dest[3] = (byte)(255); 159 | } 160 | break; 161 | case ((2) * 8 + (1)): 162 | for (i = (int)(x - 1); (i) >= (0); --i, src += 2, dest += 1) 163 | { 164 | dest[0] = (byte)(src[0]); 165 | } 166 | break; 167 | case ((2) * 8 + (3)): 168 | for (i = (int)(x - 1); (i) >= (0); --i, src += 2, dest += 3) 169 | { 170 | dest[0] = (byte)(dest[1] = (byte)(dest[2] = (byte)(src[0]))); 171 | } 172 | break; 173 | case ((2) * 8 + (4)): 174 | for (i = (int)(x - 1); (i) >= (0); --i, src += 2, dest += 4) 175 | { 176 | dest[0] = (byte)(dest[1] = (byte)(dest[2] = (byte)(src[0]))); 177 | dest[3] = (byte)(src[1]); 178 | } 179 | break; 180 | case ((3) * 8 + (4)): 181 | for (i = (int)(x - 1); (i) >= (0); --i, src += 3, dest += 4) 182 | { 183 | dest[0] = (byte)(src[0]); 184 | dest[1] = (byte)(src[1]); 185 | dest[2] = (byte)(src[2]); 186 | dest[3] = (byte)(255); 187 | } 188 | break; 189 | case ((3) * 8 + (1)): 190 | for (i = (int)(x - 1); (i) >= (0); --i, src += 3, dest += 1) 191 | { 192 | dest[0] = (byte)(stbi__compute_y((int)(src[0]), (int)(src[1]), (int)(src[2]))); 193 | } 194 | break; 195 | case ((3) * 8 + (2)): 196 | for (i = (int)(x - 1); (i) >= (0); --i, src += 3, dest += 2) 197 | { 198 | dest[0] = (byte)(stbi__compute_y((int)(src[0]), (int)(src[1]), (int)(src[2]))); 199 | dest[1] = (byte)(255); 200 | } 201 | break; 202 | case ((4) * 8 + (1)): 203 | for (i = (int)(x - 1); (i) >= (0); --i, src += 4, dest += 1) 204 | { 205 | dest[0] = (byte)(stbi__compute_y((int)(src[0]), (int)(src[1]), (int)(src[2]))); 206 | } 207 | break; 208 | case ((4) * 8 + (2)): 209 | for (i = (int)(x - 1); (i) >= (0); --i, src += 4, dest += 2) 210 | { 211 | dest[0] = (byte)(stbi__compute_y((int)(src[0]), (int)(src[1]), (int)(src[2]))); 212 | dest[1] = (byte)(src[3]); 213 | } 214 | break; 215 | case ((4) * 8 + (3)): 216 | for (i = (int)(x - 1); (i) >= (0); --i, src += 4, dest += 3) 217 | { 218 | dest[0] = (byte)(src[0]); 219 | dest[1] = (byte)(src[1]); 220 | dest[2] = (byte)(src[2]); 221 | } 222 | break; 223 | default: 224 | Decoder.stbi__err("0"); 225 | break; 226 | } 227 | } 228 | 229 | return good; 230 | } 231 | 232 | public static byte[] stbi__convert_16_to_8(byte[] orig, int w, int h, int channels) 233 | { 234 | throw new NotImplementedException(); 235 | 236 | /* int i = 0; 237 | int img_len = (int)(w * h * channels); 238 | var reduced = new byte[img_len]; 239 | 240 | fixed (byte* ptr2 = &orig[0]) 241 | { 242 | ushort* ptr = (ushort*)ptr2; 243 | for (i = (int)(0); (i) < (img_len); ++i) 244 | { 245 | reduced[i] = ((byte)((ptr[i] >> 8) & 0xFF)); 246 | } 247 | } 248 | return reduced;*/ 249 | } 250 | 251 | public static ushort[] stbi__convert_8_to_16(byte[] orig, int w, int h, int channels) 252 | { 253 | int i = 0; 254 | int img_len = (int)(w * h * channels); 255 | var enlarged = new ushort[img_len]; 256 | for (i = (int)(0); (i) < (img_len); ++i) 257 | { 258 | enlarged[i] = ((ushort)((orig[i] << 8) + orig[i])); 259 | } 260 | 261 | return enlarged; 262 | } 263 | 264 | public static void stbi__vertical_flip(byte[] image, int w, int h, int bytes_per_pixel) 265 | { 266 | int row = 0; 267 | int bytes_per_row = w * bytes_per_pixel; 268 | byte[] temp = new byte[2048]; 269 | for (row = (int)(0); (row) < (h >> 1); row++) 270 | { 271 | FakePtr row0 = new FakePtr(image, (int)(row * bytes_per_row)); 272 | FakePtr row1 = new FakePtr(image, (int)((h - row - 1) * bytes_per_row)); 273 | int bytes_left = bytes_per_row; 274 | while ((bytes_left) != 0) 275 | { 276 | int bytes_copy = (((bytes_left) < (2048)) ? bytes_left : 2048); 277 | temp.memcpy(row0, bytes_copy); 278 | row0.memcpy(row1, bytes_copy); 279 | row1.memcpy(temp, bytes_copy); 280 | row0 += bytes_copy; 281 | row1 += bytes_copy; 282 | bytes_left -= bytes_copy; 283 | } 284 | } 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/Decoding/TgaDecoder.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using StbImageSharp.Utility; 3 | 4 | namespace StbImageSharp.Decoding 5 | { 6 | #if !STBSHARP_INTERNAL 7 | public 8 | #else 9 | internal 10 | #endif 11 | class TgaDecoder : Decoder 12 | { 13 | private TgaDecoder(Stream stream) : base(stream) 14 | { 15 | } 16 | 17 | private static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, out int is_rgb16) 18 | { 19 | is_rgb16 = 0; 20 | switch (bits_per_pixel) 21 | { 22 | case 8: 23 | return 1; 24 | case 15: 25 | case 16: 26 | if (bits_per_pixel == 16 && is_grey != 0) 27 | return 2; 28 | is_rgb16 = 1; 29 | return 3; 30 | case 24: 31 | case 32: 32 | return bits_per_pixel / 8; 33 | default: 34 | return 0; 35 | } 36 | } 37 | 38 | private void stbi__tga_read_rgb16(FakePtr _out_) 39 | { 40 | var px = (ushort)stbi__get16le(); 41 | var fiveBitMask = (ushort)31; 42 | var r = (px >> 10) & fiveBitMask; 43 | var g = (px >> 5) & fiveBitMask; 44 | var b = px & fiveBitMask; 45 | _out_[0] = (byte)(r * 255 / 31); 46 | _out_[1] = (byte)(g * 255 / 31); 47 | _out_[2] = (byte)(b * 255 / 31); 48 | } 49 | 50 | private ImageResult InternalDecode(ColorComponents? requiredComponents) 51 | { 52 | var tga_offset = (int)stbi__get8(); 53 | var tga_indexed = (int)stbi__get8(); 54 | var tga_image_type = (int)stbi__get8(); 55 | var tga_is_RLE = 0; 56 | var tga_palette_start = stbi__get16le(); 57 | var tga_palette_len = stbi__get16le(); 58 | var tga_palette_bits = (int)stbi__get8(); 59 | var tga_x_origin = stbi__get16le(); 60 | var tga_y_origin = stbi__get16le(); 61 | var tga_width = stbi__get16le(); 62 | var tga_height = stbi__get16le(); 63 | var tga_bits_per_pixel = (int)stbi__get8(); 64 | var tga_comp = 0; 65 | var tga_rgb16 = 0; 66 | var tga_inverted = (int)stbi__get8(); 67 | byte[] tga_data; 68 | byte[] tga_palette = null; 69 | var i = 0; 70 | var j = 0; 71 | var raw_data = new byte[4]; 72 | raw_data[0] = 0; 73 | 74 | var RLE_count = 0; 75 | var RLE_repeating = 0; 76 | var read_next_pixel = 1; 77 | if (tga_image_type >= 8) 78 | { 79 | tga_image_type -= 8; 80 | tga_is_RLE = 1; 81 | } 82 | 83 | tga_inverted = 1 - ((tga_inverted >> 5) & 1); 84 | if (tga_indexed != 0) 85 | tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, out tga_rgb16); 86 | else 87 | tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, tga_image_type == 3 ? 1 : 0, out tga_rgb16); 88 | if (tga_comp == 0) 89 | stbi__err("bad format"); 90 | 91 | tga_data = new byte[tga_width * tga_height * tga_comp]; 92 | stbi__skip(tga_offset); 93 | if (tga_indexed == 0 && tga_is_RLE == 0 && tga_rgb16 == 0) 94 | { 95 | for (i = 0; i < tga_height; ++i) 96 | { 97 | var row = tga_inverted != 0 ? tga_height - i - 1 : i; 98 | stbi__getn(tga_data, row * tga_width * tga_comp, tga_width * tga_comp); 99 | } 100 | } 101 | else 102 | { 103 | if (tga_indexed != 0) 104 | { 105 | stbi__skip(tga_palette_start); 106 | tga_palette = new byte[tga_palette_len * tga_comp]; 107 | if (tga_rgb16 != 0) 108 | { 109 | var pal_entry = new FakePtr(tga_palette); 110 | for (i = 0; i < tga_palette_len; ++i) 111 | { 112 | stbi__tga_read_rgb16(pal_entry); 113 | pal_entry += tga_comp; 114 | } 115 | } 116 | else if (!stbi__getn(tga_palette, 0, tga_palette_len * tga_comp)) 117 | { 118 | stbi__err("bad palette"); 119 | } 120 | } 121 | 122 | for (i = 0; i < tga_width * tga_height; ++i) 123 | { 124 | if (tga_is_RLE != 0) 125 | { 126 | if (RLE_count == 0) 127 | { 128 | var RLE_cmd = (int)stbi__get8(); 129 | RLE_count = 1 + (RLE_cmd & 127); 130 | RLE_repeating = RLE_cmd >> 7; 131 | read_next_pixel = 1; 132 | } 133 | else if (RLE_repeating == 0) 134 | { 135 | read_next_pixel = 1; 136 | } 137 | } 138 | else 139 | { 140 | read_next_pixel = 1; 141 | } 142 | 143 | if (read_next_pixel != 0) 144 | { 145 | if (tga_indexed != 0) 146 | { 147 | var pal_idx = tga_bits_per_pixel == 8 ? stbi__get8() : stbi__get16le(); 148 | if (pal_idx >= tga_palette_len) pal_idx = 0; 149 | pal_idx *= tga_comp; 150 | for (j = 0; j < tga_comp; ++j) raw_data[j] = tga_palette[pal_idx + j]; 151 | } 152 | else if (tga_rgb16 != 0) 153 | { 154 | stbi__tga_read_rgb16(new FakePtr(raw_data)); 155 | } 156 | else 157 | { 158 | for (j = 0; j < tga_comp; ++j) raw_data[j] = stbi__get8(); 159 | } 160 | 161 | read_next_pixel = 0; 162 | } 163 | 164 | for (j = 0; j < tga_comp; ++j) tga_data[i * tga_comp + j] = raw_data[j]; 165 | --RLE_count; 166 | } 167 | 168 | if (tga_inverted != 0) 169 | for (j = 0; j * 2 < tga_height; ++j) 170 | { 171 | var index1 = j * tga_width * tga_comp; 172 | var index2 = (tga_height - 1 - j) * tga_width * tga_comp; 173 | for (i = tga_width * tga_comp; i > 0; --i) 174 | { 175 | var temp = tga_data[index1]; 176 | tga_data[index1] = tga_data[index2]; 177 | tga_data[index2] = temp; 178 | ++index1; 179 | ++index2; 180 | } 181 | } 182 | } 183 | 184 | if (tga_comp >= 3 && tga_rgb16 == 0) 185 | { 186 | var tga_pixel = new FakePtr(tga_data); 187 | for (i = 0; i < tga_width * tga_height; ++i) 188 | { 189 | var temp = tga_pixel[0]; 190 | tga_pixel[0] = tga_pixel[2]; 191 | tga_pixel[2] = temp; 192 | tga_pixel += tga_comp; 193 | } 194 | } 195 | 196 | var req_comp = requiredComponents.ToReqComp(); 197 | if (req_comp != 0 && req_comp != tga_comp) 198 | tga_data = Conversion.stbi__convert_format(tga_data, tga_comp, req_comp, (uint)tga_width, 199 | (uint)tga_height); 200 | tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; 201 | 202 | return new ImageResult 203 | { 204 | Width = tga_width, 205 | Height = tga_height, 206 | SourceComponents = (ColorComponents)tga_comp, 207 | ColorComponents = requiredComponents != null ? requiredComponents.Value : (ColorComponents)tga_comp, 208 | BitsPerChannel = 8, 209 | Data = tga_data 210 | }; 211 | } 212 | 213 | public static bool Test(Stream stream) 214 | { 215 | try 216 | { 217 | stream.stbi__get8(); 218 | var tga_color_type = (int)stream.stbi__get8(); 219 | if (tga_color_type > 1) 220 | return false; 221 | var sz = (int)stream.stbi__get8(); 222 | if (tga_color_type == 1) 223 | { 224 | if (sz != 1 && sz != 9) 225 | return false; 226 | stream.stbi__skip(4); 227 | sz = stream.stbi__get8(); 228 | if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) 229 | return false; 230 | stream.stbi__skip(4); 231 | } 232 | else 233 | { 234 | if (sz != 2 && sz != 3 && sz != 10 && sz != 11) 235 | return false; 236 | stream.stbi__skip(9); 237 | } 238 | 239 | if (stream.stbi__get16le() < 1) 240 | return false; 241 | if (stream.stbi__get16le() < 1) 242 | return false; 243 | sz = stream.stbi__get8(); 244 | if (tga_color_type == 1 && sz != 8 && sz != 16) 245 | return false; 246 | if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) 247 | return false; 248 | 249 | return true; 250 | } 251 | finally 252 | { 253 | stream.Rewind(); 254 | } 255 | } 256 | 257 | public static ImageInfo? Info(Stream stream) 258 | { 259 | try 260 | { 261 | var tga_w = 0; 262 | var tga_h = 0; 263 | var tga_comp = 0; 264 | var tga_image_type = 0; 265 | var tga_bits_per_pixel = 0; 266 | var tga_colormap_bpp = 0; 267 | var sz = 0; 268 | var tga_colormap_type = 0; 269 | stream.stbi__get8(); 270 | tga_colormap_type = stream.stbi__get8(); 271 | if (tga_colormap_type > 1) return null; 272 | 273 | tga_image_type = stream.stbi__get8(); 274 | if (tga_colormap_type == 1) 275 | { 276 | if (tga_image_type != 1 && tga_image_type != 9) return null; 277 | stream.stbi__skip(4); 278 | sz = stream.stbi__get8(); 279 | if (sz != 8 && sz != 15 && sz != 16 && sz != 24 && sz != 32) return null; 280 | stream.stbi__skip(4); 281 | tga_colormap_bpp = sz; 282 | } 283 | else 284 | { 285 | if (tga_image_type != 2 && tga_image_type != 3 && tga_image_type != 10 && tga_image_type != 11) 286 | return null; 287 | stream.stbi__skip(9); 288 | tga_colormap_bpp = 0; 289 | } 290 | 291 | tga_w = stream.stbi__get16le(); 292 | if (tga_w < 1) return null; 293 | 294 | tga_h = stream.stbi__get16le(); 295 | if (tga_h < 1) return null; 296 | 297 | tga_bits_per_pixel = stream.stbi__get8(); 298 | stream.stbi__get8(); 299 | int is_rgb16; 300 | if (tga_colormap_bpp != 0) 301 | { 302 | if (tga_bits_per_pixel != 8 && tga_bits_per_pixel != 16) return null; 303 | tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, out is_rgb16); 304 | } 305 | else 306 | { 307 | tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, 308 | tga_image_type == 3 || tga_image_type == 11 ? 1 : 0, out is_rgb16); 309 | } 310 | 311 | if (tga_comp == 0) return null; 312 | 313 | return new ImageInfo 314 | { 315 | Width = tga_w, 316 | Height = tga_h, 317 | ColorComponents = (ColorComponents)tga_comp, 318 | BitsPerChannel = tga_bits_per_pixel 319 | }; 320 | } 321 | finally 322 | { 323 | stream.Rewind(); 324 | } 325 | } 326 | 327 | public static ImageResult Decode(Stream stream, ColorComponents? requiredComponents = null) 328 | { 329 | var decoder = new TgaDecoder(stream); 330 | return decoder.InternalDecode(requiredComponents); 331 | } 332 | } 333 | } -------------------------------------------------------------------------------- /src/Decoding/BmpDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using StbImageSharp.Utility; 5 | 6 | namespace StbImageSharp.Decoding 7 | { 8 | #if !STBSHARP_INTERNAL 9 | public 10 | #else 11 | internal 12 | #endif 13 | class BmpDecoder : Decoder 14 | { 15 | [StructLayout(LayoutKind.Sequential)] 16 | public struct stbi__bmp_data 17 | { 18 | public int bpp; 19 | public int offset; 20 | public int hsz; 21 | public uint mr; 22 | public uint mg; 23 | public uint mb; 24 | public uint ma; 25 | public uint all_a; 26 | } 27 | 28 | private static readonly uint[] mul_table = { 0, 0xff, 0x55, 0x49, 0x11, 0x21, 0x41, 0x81, 0x01 }; 29 | private static readonly uint[] shift_table = { 0, 0, 0, 1, 0, 2, 4, 6, 0 }; 30 | 31 | private BmpDecoder(Stream stream) : base(stream) 32 | { 33 | } 34 | 35 | private static int stbi__high_bit(uint z) 36 | { 37 | var n = 0; 38 | if (z == 0) 39 | return -1; 40 | if (z >= 0x10000) 41 | { 42 | n += 16; 43 | z >>= 16; 44 | } 45 | 46 | if (z >= 0x00100) 47 | { 48 | n += 8; 49 | z >>= 8; 50 | } 51 | 52 | if (z >= 0x00010) 53 | { 54 | n += 4; 55 | z >>= 4; 56 | } 57 | 58 | if (z >= 0x00004) 59 | { 60 | n += 2; 61 | z >>= 2; 62 | } 63 | 64 | if (z >= 0x00002) 65 | { 66 | n += 1; 67 | z >>= 1; 68 | } 69 | 70 | return n; 71 | } 72 | 73 | private static int stbi__bitcount(uint a) 74 | { 75 | a = (a & 0x55555555) + ((a >> 1) & 0x55555555); 76 | a = (a & 0x33333333) + ((a >> 2) & 0x33333333); 77 | a = (a + (a >> 4)) & 0x0f0f0f0f; 78 | a = a + (a >> 8); 79 | a = a + (a >> 16); 80 | return (int)(a & 0xff); 81 | } 82 | 83 | private static int stbi__shiftsigned(uint v, int shift, int bits) 84 | { 85 | if (shift < 0) 86 | v <<= -shift; 87 | else 88 | v >>= shift; 89 | v >>= 8 - bits; 90 | return (int)(v * (int)mul_table[bits]) >> (int)shift_table[bits]; 91 | } 92 | 93 | private void stbi__bmp_parse_header(ref stbi__bmp_data info) 94 | { 95 | var hsz = 0; 96 | if (stbi__get8() != 'B' || stbi__get8() != 'M') 97 | stbi__err("not BMP"); 98 | stbi__get32le(); 99 | stbi__get16le(); 100 | stbi__get16le(); 101 | info.offset = (int)stbi__get32le(); 102 | info.hsz = hsz = (int)stbi__get32le(); 103 | info.mr = info.mg = info.mb = info.ma = 0; 104 | if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) 105 | stbi__err("unknown BMP"); 106 | if (hsz == 12) 107 | { 108 | img_x = stbi__get16le(); 109 | img_y = stbi__get16le(); 110 | } 111 | else 112 | { 113 | img_x = (int)stbi__get32le(); 114 | img_y = (int)stbi__get32le(); 115 | } 116 | 117 | if (stbi__get16le() != 1) 118 | stbi__err("bad BMP"); 119 | info.bpp = stbi__get16le(); 120 | if (hsz != 12) 121 | { 122 | var compress = (int)stbi__get32le(); 123 | if (compress == 1 || compress == 2) 124 | stbi__err("BMP RLE"); 125 | stbi__get32le(); 126 | stbi__get32le(); 127 | stbi__get32le(); 128 | stbi__get32le(); 129 | stbi__get32le(); 130 | if (hsz == 40 || hsz == 56) 131 | { 132 | if (hsz == 56) 133 | { 134 | stbi__get32le(); 135 | stbi__get32le(); 136 | stbi__get32le(); 137 | stbi__get32le(); 138 | } 139 | 140 | if (info.bpp == 16 || info.bpp == 32) 141 | { 142 | if (compress == 0) 143 | { 144 | if (info.bpp == 32) 145 | { 146 | info.mr = 0xffu << 16; 147 | info.mg = 0xffu << 8; 148 | info.mb = 0xffu << 0; 149 | info.ma = 0xffu << 24; 150 | info.all_a = 0; 151 | } 152 | else 153 | { 154 | info.mr = 31u << 10; 155 | info.mg = 31u << 5; 156 | info.mb = 31u << 0; 157 | } 158 | } 159 | else if (compress == 3) 160 | { 161 | info.mr = stbi__get32le(); 162 | info.mg = stbi__get32le(); 163 | info.mb = stbi__get32le(); 164 | if (info.mr == info.mg && info.mg == info.mb) stbi__err("bad BMP"); 165 | } 166 | else 167 | { 168 | stbi__err("bad BMP"); 169 | } 170 | } 171 | } 172 | else 173 | { 174 | var i = 0; 175 | if (hsz != 108 && hsz != 124) 176 | stbi__err("bad BMP"); 177 | info.mr = stbi__get32le(); 178 | info.mg = stbi__get32le(); 179 | info.mb = stbi__get32le(); 180 | info.ma = stbi__get32le(); 181 | stbi__get32le(); 182 | for (i = 0; i < 12; ++i) stbi__get32le(); 183 | if (hsz == 124) 184 | { 185 | stbi__get32le(); 186 | stbi__get32le(); 187 | stbi__get32le(); 188 | stbi__get32le(); 189 | } 190 | } 191 | } 192 | } 193 | 194 | private ImageResult InternalDecode(ColorComponents? requiredComponents) 195 | { 196 | byte[] _out_; 197 | var mr = (uint)0; 198 | var mg = (uint)0; 199 | var mb = (uint)0; 200 | var ma = (uint)0; 201 | uint all_a = 0; 202 | var pal = new byte[256 * 4]; 203 | var psize = 0; 204 | var i = 0; 205 | var j = 0; 206 | var width = 0; 207 | var flip_vertically = 0; 208 | var pad = 0; 209 | var target = 0; 210 | var info = new stbi__bmp_data(); 211 | info.all_a = 255; 212 | stbi__bmp_parse_header(ref info); 213 | flip_vertically = img_y > 0 ? 1 : 0; 214 | img_y = Math.Abs(img_y); 215 | mr = info.mr; 216 | mg = info.mg; 217 | mb = info.mb; 218 | ma = info.ma; 219 | all_a = info.all_a; 220 | if (info.hsz == 12) 221 | { 222 | if (info.bpp < 24) 223 | psize = (info.offset - 14 - 24) / 3; 224 | } 225 | else 226 | { 227 | if (info.bpp < 16) 228 | psize = (info.offset - 14 - info.hsz) >> 2; 229 | } 230 | 231 | img_n = ma != 0 ? 4 : 3; 232 | if (requiredComponents != null && (int)requiredComponents.Value >= 3) 233 | target = (int)requiredComponents.Value; 234 | else 235 | target = img_n; 236 | _out_ = new byte[target * img_x * img_y]; 237 | if (info.bpp < 16) 238 | { 239 | var z = 0; 240 | if (psize == 0 || psize > 256) stbi__err("invalid"); 241 | for (i = 0; i < psize; ++i) 242 | { 243 | pal[i * 4 + 2] = stbi__get8(); 244 | pal[i * 4 + 1] = stbi__get8(); 245 | pal[i * 4 + 0] = stbi__get8(); 246 | if (info.hsz != 12) 247 | stbi__get8(); 248 | pal[i * 4 + 3] = 255; 249 | } 250 | 251 | stbi__skip(info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); 252 | if (info.bpp == 1) 253 | width = (img_x + 7) >> 3; 254 | else if (info.bpp == 4) 255 | width = (img_x + 1) >> 1; 256 | else if (info.bpp == 8) 257 | width = img_x; 258 | else 259 | stbi__err("bad bpp"); 260 | pad = -width & 3; 261 | if (info.bpp == 1) 262 | for (j = 0; j < img_y; ++j) 263 | { 264 | var bit_offset = 7; 265 | var v = (int)stbi__get8(); 266 | for (i = 0; i < img_x; ++i) 267 | { 268 | var color = (v >> bit_offset) & 0x1; 269 | _out_[z++] = pal[color * 4 + 0]; 270 | _out_[z++] = pal[color * 4 + 1]; 271 | _out_[z++] = pal[color * 4 + 2]; 272 | if (target == 4) 273 | _out_[z++] = 255; 274 | if (i + 1 == img_x) 275 | break; 276 | if (--bit_offset < 0) 277 | { 278 | bit_offset = 7; 279 | v = stbi__get8(); 280 | } 281 | } 282 | 283 | stbi__skip(pad); 284 | } 285 | else 286 | for (j = 0; j < img_y; ++j) 287 | { 288 | for (i = 0; i < img_x; i += 2) 289 | { 290 | var v = (int)stbi__get8(); 291 | var v2 = 0; 292 | if (info.bpp == 4) 293 | { 294 | v2 = v & 15; 295 | v >>= 4; 296 | } 297 | 298 | _out_[z++] = pal[v * 4 + 0]; 299 | _out_[z++] = pal[v * 4 + 1]; 300 | _out_[z++] = pal[v * 4 + 2]; 301 | if (target == 4) 302 | _out_[z++] = 255; 303 | if (i + 1 == img_x) 304 | break; 305 | v = info.bpp == 8 ? stbi__get8() : v2; 306 | _out_[z++] = pal[v * 4 + 0]; 307 | _out_[z++] = pal[v * 4 + 1]; 308 | _out_[z++] = pal[v * 4 + 2]; 309 | if (target == 4) 310 | _out_[z++] = 255; 311 | } 312 | 313 | stbi__skip(pad); 314 | } 315 | } 316 | else 317 | { 318 | var rshift = 0; 319 | var gshift = 0; 320 | var bshift = 0; 321 | var ashift = 0; 322 | var rcount = 0; 323 | var gcount = 0; 324 | var bcount = 0; 325 | var acount = 0; 326 | var z = 0; 327 | var easy = 0; 328 | stbi__skip(info.offset - 14 - info.hsz); 329 | if (info.bpp == 24) 330 | width = 3 * img_x; 331 | else if (info.bpp == 16) 332 | width = 2 * img_x; 333 | else 334 | width = 0; 335 | pad = -width & 3; 336 | if (info.bpp == 24) 337 | easy = 1; 338 | else if (info.bpp == 32) 339 | if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) 340 | easy = 2; 341 | if (easy == 0) 342 | { 343 | if (mr == 0 || mg == 0 || mb == 0) stbi__err("bad masks"); 344 | rshift = stbi__high_bit(mr) - 7; 345 | rcount = stbi__bitcount(mr); 346 | gshift = stbi__high_bit(mg) - 7; 347 | gcount = stbi__bitcount(mg); 348 | bshift = stbi__high_bit(mb) - 7; 349 | bcount = stbi__bitcount(mb); 350 | ashift = stbi__high_bit(ma) - 7; 351 | acount = stbi__bitcount(ma); 352 | } 353 | 354 | for (j = 0; j < img_y; ++j) 355 | { 356 | if (easy != 0) 357 | { 358 | for (i = 0; i < img_x; ++i) 359 | { 360 | byte a = 0; 361 | _out_[z + 2] = stbi__get8(); 362 | _out_[z + 1] = stbi__get8(); 363 | _out_[z + 0] = stbi__get8(); 364 | z += 3; 365 | a = (byte)(easy == 2 ? stbi__get8() : 255); 366 | all_a |= a; 367 | if (target == 4) 368 | _out_[z++] = a; 369 | } 370 | } 371 | else 372 | { 373 | var bpp = info.bpp; 374 | for (i = 0; i < img_x; ++i) 375 | { 376 | var v = bpp == 16 ? (uint)stbi__get16le() : stbi__get32le(); 377 | uint a = 0; 378 | _out_[z++] = (byte)(stbi__shiftsigned(v & mr, rshift, rcount) & 255); 379 | _out_[z++] = (byte)(stbi__shiftsigned(v & mg, gshift, gcount) & 255); 380 | _out_[z++] = (byte)(stbi__shiftsigned(v & mb, bshift, bcount) & 255); 381 | a = (uint)(ma != 0 ? stbi__shiftsigned(v & ma, ashift, acount) : 255); 382 | all_a |= a; 383 | if (target == 4) 384 | _out_[z++] = (byte)(a & 255); 385 | } 386 | } 387 | 388 | stbi__skip(pad); 389 | } 390 | } 391 | 392 | if (target == 4 && all_a == 0) 393 | for (i = 4 * img_x * img_y - 1; i >= 0; i -= 4) 394 | _out_[i] = 255; 395 | if (flip_vertically != 0) 396 | { 397 | byte t = 0; 398 | var ptr = new FakePtr(_out_); 399 | for (j = 0; j < img_y >> 1; ++j) 400 | { 401 | var p1 = ptr + j * img_x * target; 402 | var p2 = ptr + (img_y - 1 - j) * img_x * target; 403 | for (i = 0; i < img_x * target; ++i) 404 | { 405 | t = p1[i]; 406 | p1[i] = p2[i]; 407 | p2[i] = t; 408 | } 409 | } 410 | } 411 | 412 | if (requiredComponents != null && (int)requiredComponents.Value != target) 413 | _out_ = Conversion.stbi__convert_format(_out_, target, (int)requiredComponents.Value, (uint)img_x, 414 | (uint)img_y); 415 | 416 | return new ImageResult 417 | { 418 | Width = img_x, 419 | Height = img_y, 420 | SourceComponents = (ColorComponents)img_n, 421 | ColorComponents = requiredComponents != null ? requiredComponents.Value : (ColorComponents)img_n, 422 | BitsPerChannel = 8, 423 | Data = _out_ 424 | }; 425 | } 426 | 427 | private static bool TestInternal(Stream stream) 428 | { 429 | var sz = 0; 430 | if (stream.ReadByte() != 'B') 431 | return false; 432 | if (stream.ReadByte() != 'M') 433 | return false; 434 | 435 | stream.stbi__get32le(); 436 | stream.stbi__get16le(); 437 | stream.stbi__get16le(); 438 | stream.stbi__get32le(); 439 | sz = (int)stream.stbi__get32le(); 440 | var r = sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124; 441 | return r; 442 | } 443 | 444 | public static bool Test(Stream stream) 445 | { 446 | var r = TestInternal(stream); 447 | stream.Rewind(); 448 | return r; 449 | } 450 | 451 | public static ImageInfo? Info(Stream stream) 452 | { 453 | var info = new stbi__bmp_data 454 | { 455 | all_a = 255 456 | }; 457 | 458 | var decoder = new BmpDecoder(stream); 459 | try 460 | { 461 | decoder.stbi__bmp_parse_header(ref info); 462 | } 463 | catch (Exception) 464 | { 465 | return null; 466 | } 467 | finally 468 | { 469 | stream.Rewind(); 470 | } 471 | 472 | return new ImageInfo 473 | { 474 | Width = decoder.img_x, 475 | Height = decoder.img_y, 476 | ColorComponents = info.ma != 0 ? ColorComponents.RedGreenBlueAlpha : ColorComponents.RedGreenBlue, 477 | BitsPerChannel = 8 478 | }; 479 | } 480 | 481 | public static ImageResult Decode(Stream stream, ColorComponents? requiredComponents = null) 482 | { 483 | var decoder = new BmpDecoder(stream); 484 | return decoder.InternalDecode(requiredComponents); 485 | } 486 | } 487 | } -------------------------------------------------------------------------------- /src/Decoding/ZLib.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using StbImageSharp.Utility; 3 | 4 | namespace StbImageSharp.Decoding 5 | { 6 | internal class ZLib 7 | { 8 | private class stbi__zhuffman 9 | { 10 | public readonly ushort[] fast = new ushort[1 << 9]; 11 | public readonly ushort[] firstcode = new ushort[16]; 12 | public readonly ushort[] firstsymbol = new ushort[16]; 13 | public readonly int[] maxcode = new int[17]; 14 | public readonly byte[] size = new byte[288]; 15 | public readonly ushort[] value = new ushort[288]; 16 | } 17 | 18 | private static readonly int[] stbi__zlength_base = 19 | { 20 | 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 21 | 227, 258, 0, 0 22 | }; 23 | 24 | private static readonly int[] stbi__zlength_extra = 25 | {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0}; 26 | 27 | private static readonly int[] stbi__zdist_base = 28 | { 29 | 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 30 | 6145, 8193, 12289, 16385, 24577, 0, 0 31 | }; 32 | 33 | private static readonly int[] stbi__zdist_extra = 34 | {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; 35 | 36 | private static readonly byte[] stbi__zdefault_length = 37 | { 38 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 39 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 40 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 41 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 42 | 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 43 | 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 44 | 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 45 | 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8 46 | }; 47 | 48 | private static readonly byte[] stbi__zdefault_distance = 49 | {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; 50 | 51 | private static readonly byte[] length_dezigzag = 52 | {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; 53 | 54 | private uint code_buffer; 55 | private int num_bits; 56 | private readonly stbi__zhuffman z_distance = new stbi__zhuffman(); 57 | private int z_expandable; 58 | private readonly stbi__zhuffman z_length = new stbi__zhuffman(); 59 | 60 | private FakePtr zbuffer; 61 | private FakePtr zbuffer_end; 62 | private FakePtr zout; 63 | private FakePtr zout_end; 64 | private byte[] zout_start; 65 | 66 | private byte stbi__zget8() 67 | { 68 | if (zbuffer.Offset >= zbuffer_end.Offset) 69 | return 0; 70 | return zbuffer.GetAndIncrease(); 71 | } 72 | 73 | private void stbi__fill_bits() 74 | { 75 | do 76 | { 77 | code_buffer |= (uint)stbi__zget8() << num_bits; 78 | num_bits += 8; 79 | } while (num_bits <= 24); 80 | } 81 | 82 | private uint stbi__zreceive(int n) 83 | { 84 | uint k = 0; 85 | if (num_bits < n) 86 | stbi__fill_bits(); 87 | k = (uint)(code_buffer & ((1 << n) - 1)); 88 | code_buffer >>= n; 89 | num_bits -= n; 90 | return k; 91 | } 92 | 93 | private int stbi__zhuffman_decode_slowpath(stbi__zhuffman z) 94 | { 95 | var b = 0; 96 | var s = 0; 97 | var k = 0; 98 | k = MathExtensions.stbi__bit_reverse((int)code_buffer, 16); 99 | for (s = 9 + 1; ; ++s) 100 | if (k < z.maxcode[s]) 101 | break; 102 | if (s == 16) 103 | return -1; 104 | b = (k >> (16 - s)) - z.firstcode[s] + z.firstsymbol[s]; 105 | code_buffer >>= s; 106 | num_bits -= s; 107 | return z.value[b]; 108 | } 109 | 110 | private int stbi__zhuffman_decode(stbi__zhuffman z) 111 | { 112 | var b = 0; 113 | var s = 0; 114 | if (num_bits < 16) 115 | stbi__fill_bits(); 116 | b = z.fast[code_buffer & ((1 << 9) - 1)]; 117 | if (b != 0) 118 | { 119 | s = b >> 9; 120 | code_buffer >>= s; 121 | num_bits -= s; 122 | return b & 511; 123 | } 124 | 125 | return stbi__zhuffman_decode_slowpath(z); 126 | } 127 | 128 | private int stbi__zexpand(FakePtr zout, int n) 129 | { 130 | var cur = 0; 131 | var limit = 0; 132 | var old_limit = 0; 133 | this.zout = zout; 134 | if (z_expandable == 0) 135 | Decoder.stbi__err("output buffer limit"); 136 | cur = this.zout.Offset; 137 | limit = old_limit = zout_end.Offset; 138 | while (cur + n > limit) limit *= 2; 139 | 140 | Array.Resize(ref zout_start, limit); 141 | this.zout = new FakePtr(zout_start, cur); 142 | zout_end = new FakePtr(zout_start, limit); 143 | return 1; 144 | } 145 | 146 | private int stbi__parse_huffman_block() 147 | { 148 | var zout = this.zout; 149 | for (; ; ) 150 | { 151 | var z = stbi__zhuffman_decode(z_length); 152 | if (z < 256) 153 | { 154 | if (z < 0) 155 | Decoder.stbi__err("bad huffman code"); 156 | if (zout.Offset >= zout_end.Offset) 157 | { 158 | if (stbi__zexpand(zout, 1) == 0) 159 | return 0; 160 | zout = this.zout; 161 | } 162 | 163 | zout.SetAndIncrease((byte)z); 164 | } 165 | else 166 | { 167 | var len = 0; 168 | var dist = 0; 169 | if (z == 256) 170 | { 171 | this.zout = zout; 172 | return 1; 173 | } 174 | 175 | z -= 257; 176 | len = stbi__zlength_base[z]; 177 | if (stbi__zlength_extra[z] != 0) 178 | len += (int)stbi__zreceive(stbi__zlength_extra[z]); 179 | z = stbi__zhuffman_decode(z_distance); 180 | if (z < 0) 181 | Decoder.stbi__err("bad huffman code"); 182 | dist = stbi__zdist_base[z]; 183 | if (stbi__zdist_extra[z] != 0) 184 | dist += (int)stbi__zreceive(stbi__zdist_extra[z]); 185 | if (zout.Offset < dist) 186 | Decoder.stbi__err("bad dist"); 187 | if (zout.Offset + len > zout_end.Offset) 188 | { 189 | if (stbi__zexpand(zout, len) == 0) 190 | return 0; 191 | zout = this.zout; 192 | } 193 | 194 | var p = new FakePtr(zout, -dist); 195 | if (dist == 1) 196 | { 197 | var v = p.Value; 198 | if (len > 0) 199 | { 200 | zout.memset(v, len); 201 | zout += len; 202 | } 203 | } 204 | else 205 | { 206 | if (len != 0) 207 | do 208 | { 209 | zout.SetAndIncrease(p.GetAndIncrease()); 210 | } while (--len != 0); 211 | } 212 | } 213 | } 214 | } 215 | 216 | private static int stbi__zbuild_huffman(stbi__zhuffman z, FakePtr sizelist, int num) 217 | { 218 | var i = 0; 219 | var k = 0; 220 | var code = 0; 221 | var next_code = new int[16]; 222 | var sizes = new int[17]; 223 | sizes.Clear(); 224 | z.fast.Clear(); 225 | for (i = 0; i < num; ++i) ++sizes[sizelist[i]]; 226 | sizes[0] = 0; 227 | for (i = 1; i < 16; ++i) 228 | if (sizes[i] > 1 << i) 229 | Decoder.stbi__err("bad sizes"); 230 | code = 0; 231 | for (i = 1; i < 16; ++i) 232 | { 233 | next_code[i] = code; 234 | z.firstcode[i] = (ushort)code; 235 | z.firstsymbol[i] = (ushort)k; 236 | code = code + sizes[i]; 237 | if (sizes[i] != 0) 238 | if (code - 1 >= 1 << i) 239 | Decoder.stbi__err("bad codelengths"); 240 | z.maxcode[i] = code << (16 - i); 241 | code <<= 1; 242 | k += sizes[i]; 243 | } 244 | 245 | z.maxcode[16] = 0x10000; 246 | for (i = 0; i < num; ++i) 247 | { 248 | var s = (int)sizelist[i]; 249 | if (s != 0) 250 | { 251 | var c = next_code[s] - z.firstcode[s] + z.firstsymbol[s]; 252 | var fastv = (ushort)((s << 9) | i); 253 | z.size[c] = (byte)s; 254 | z.value[c] = (ushort)i; 255 | if (s <= 9) 256 | { 257 | var j = MathExtensions.stbi__bit_reverse(next_code[s], s); 258 | while (j < 1 << 9) 259 | { 260 | z.fast[j] = fastv; 261 | j += 1 << s; 262 | } 263 | } 264 | 265 | ++next_code[s]; 266 | } 267 | } 268 | 269 | return 1; 270 | } 271 | 272 | private int stbi__compute_huffman_codes() 273 | { 274 | var z_codelength = new stbi__zhuffman(); 275 | var lencodes = new byte[286 + 32 + 137]; 276 | var codelength_sizes = new byte[19]; 277 | var i = 0; 278 | var n = 0; 279 | var hlit = (int)(stbi__zreceive(5) + 257); 280 | var hdist = (int)(stbi__zreceive(5) + 1); 281 | var hclen = (int)(stbi__zreceive(4) + 4); 282 | var ntot = hlit + hdist; 283 | codelength_sizes.Clear(); 284 | for (i = 0; i < hclen; ++i) 285 | { 286 | var s = (int)stbi__zreceive(3); 287 | codelength_sizes[length_dezigzag[i]] = (byte)s; 288 | } 289 | 290 | if (stbi__zbuild_huffman(z_codelength, new FakePtr(codelength_sizes), 19) == 0) 291 | return 0; 292 | n = 0; 293 | while (n < ntot) 294 | { 295 | var c = stbi__zhuffman_decode(z_codelength); 296 | if (c < 0 || c >= 19) 297 | Decoder.stbi__err("bad codelengths"); 298 | if (c < 16) 299 | { 300 | lencodes[n++] = (byte)c; 301 | } 302 | else 303 | { 304 | var fill = (byte)0; 305 | if (c == 16) 306 | { 307 | c = (int)(stbi__zreceive(2) + 3); 308 | if (n == 0) 309 | Decoder.stbi__err("bad codelengths"); 310 | fill = lencodes[n - 1]; 311 | } 312 | else if (c == 17) 313 | { 314 | c = (int)(stbi__zreceive(3) + 3); 315 | } 316 | else 317 | { 318 | c = (int)(stbi__zreceive(7) + 11); 319 | } 320 | 321 | if (ntot - n < c) 322 | Decoder.stbi__err("bad codelengths"); 323 | lencodes.Set(n, c, fill); 324 | n += c; 325 | } 326 | } 327 | 328 | if (n != ntot) 329 | Decoder.stbi__err("bad codelengths"); 330 | if (stbi__zbuild_huffman(z_length, new FakePtr(lencodes), hlit) == 0) 331 | return 0; 332 | if (stbi__zbuild_huffman(z_distance, new FakePtr(lencodes, hlit), hdist) == 0) 333 | return 0; 334 | return 1; 335 | } 336 | 337 | private int stbi__parse_uncompressed_block() 338 | { 339 | var header = new byte[4]; 340 | var len = 0; 341 | var nlen = 0; 342 | var k = 0; 343 | if ((num_bits & 7) != 0) 344 | stbi__zreceive(num_bits & 7); 345 | k = 0; 346 | while (num_bits > 0) 347 | { 348 | header[k++] = (byte)(code_buffer & 255); 349 | code_buffer >>= 8; 350 | num_bits -= 8; 351 | } 352 | 353 | while (k < 4) header[k++] = stbi__zget8(); 354 | len = header[1] * 256 + header[0]; 355 | nlen = header[3] * 256 + header[2]; 356 | if (nlen != (len ^ 0xffff)) 357 | Decoder.stbi__err("zlib corrupt"); 358 | if (zbuffer.Offset + len > zbuffer_end.Offset) 359 | Decoder.stbi__err("read past buffer"); 360 | if (zout.Offset + len > zout_end.Offset) 361 | if (stbi__zexpand(zout, len) == 0) 362 | return 0; 363 | for (var i = 0; i < len; i++) zout[i] = zbuffer[i]; 364 | zbuffer += len; 365 | zout += len; 366 | return 1; 367 | } 368 | 369 | private int stbi__parse_zlib_header() 370 | { 371 | var cmf = (int)stbi__zget8(); 372 | var cm = cmf & 15; 373 | var flg = (int)stbi__zget8(); 374 | if ((cmf * 256 + flg) % 31 != 0) 375 | Decoder.stbi__err("bad zlib header"); 376 | if ((flg & 32) != 0) 377 | Decoder.stbi__err("no preset dict"); 378 | if (cm != 8) 379 | Decoder.stbi__err("bad compression"); 380 | return 1; 381 | } 382 | 383 | private int stbi__parse_zlib(int parse_header) 384 | { 385 | var final = 0; 386 | var type = 0; 387 | if (parse_header != 0) 388 | if (stbi__parse_zlib_header() == 0) 389 | return 0; 390 | num_bits = 0; 391 | code_buffer = 0; 392 | do 393 | { 394 | final = (int)stbi__zreceive(1); 395 | type = (int)stbi__zreceive(2); 396 | if (type == 0) 397 | { 398 | if (stbi__parse_uncompressed_block() == 0) 399 | return 0; 400 | } 401 | else if (type == 3) 402 | { 403 | return 0; 404 | } 405 | else 406 | { 407 | if (type == 1) 408 | { 409 | if (stbi__zbuild_huffman(z_length, new FakePtr(stbi__zdefault_length), 288) == 0) 410 | return 0; 411 | if (stbi__zbuild_huffman(z_distance, new FakePtr(stbi__zdefault_distance), 32) == 0) 412 | return 0; 413 | } 414 | else 415 | { 416 | if (stbi__compute_huffman_codes() == 0) 417 | return 0; 418 | } 419 | 420 | if (stbi__parse_huffman_block() == 0) 421 | return 0; 422 | } 423 | } while (final == 0); 424 | 425 | return 1; 426 | } 427 | 428 | private int stbi__do_zlib(byte[] obuf, int olen, int exp, int parse_header) 429 | { 430 | zout_start = obuf; 431 | zout = new FakePtr(obuf); 432 | zout_end = new FakePtr(obuf, olen); 433 | z_expandable = exp; 434 | return stbi__parse_zlib(parse_header); 435 | } 436 | 437 | public static byte[] stbi_zlib_decode_malloc_guesssize_headerflag(byte[] buffer, int len, int initial_size, 438 | out int outlen, int parse_header) 439 | { 440 | outlen = 0; 441 | var a = new ZLib(); 442 | var p = new byte[initial_size]; 443 | a.zbuffer = new FakePtr(buffer); 444 | a.zbuffer_end = new FakePtr(buffer, +len); 445 | if (a.stbi__do_zlib(p, initial_size, 1, parse_header) != 0) 446 | { 447 | outlen = a.zout.Offset; 448 | return a.zout_start; 449 | } 450 | 451 | return null; 452 | } 453 | } 454 | } -------------------------------------------------------------------------------- /src/Decoding/GifDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using StbImageSharp.Utility; 5 | 6 | namespace StbImageSharp.Decoding 7 | { 8 | #if !STBSHARP_INTERNAL 9 | public 10 | #else 11 | internal 12 | #endif 13 | class GifDecoder : Decoder 14 | { 15 | [StructLayout(LayoutKind.Sequential)] 16 | private struct stbi__gif_lzw 17 | { 18 | public short prefix; 19 | public byte first; 20 | public byte suffix; 21 | } 22 | 23 | private int w; 24 | private int h; 25 | private byte[] _out_; 26 | private byte[] background; 27 | private byte[] history; 28 | private int flags; 29 | private int bgindex; 30 | private int ratio; 31 | private int transparent; 32 | private int eflags; 33 | private int delay; 34 | private readonly byte[] pal; 35 | private readonly byte[] lpal; 36 | private readonly stbi__gif_lzw[] codes = new stbi__gif_lzw[8192]; 37 | private byte[] color_table; 38 | private int parse; 39 | private int step; 40 | private int lflags; 41 | private int start_x; 42 | private int start_y; 43 | private int max_x; 44 | private int max_y; 45 | private int cur_x; 46 | private int cur_y; 47 | private int line_size; 48 | 49 | private GifDecoder(Stream stream) : base(stream) 50 | { 51 | pal = new byte[256 * 4]; 52 | lpal = new byte[256 * 4]; 53 | } 54 | 55 | private void stbi__gif_parse_colortable(byte[] pal, int num_entries, int transp) 56 | { 57 | int i; 58 | for (i = 0; i < num_entries; ++i) 59 | { 60 | pal[i * 4 + 2] = stbi__get8(); 61 | pal[i * 4 + 1] = stbi__get8(); 62 | pal[i * 4] = stbi__get8(); 63 | pal[i * 4 + 3] = (byte)(transp == i ? 0 : 255); 64 | } 65 | } 66 | 67 | private int stbi__gif_header(out int comp, int is_info) 68 | { 69 | byte version = 0; 70 | if (stbi__get8() != 'G' || stbi__get8() != 'I' || stbi__get8() != 'F' || stbi__get8() != '8') 71 | stbi__err("not GIF"); 72 | version = stbi__get8(); 73 | if (version != '7' && version != '9') 74 | stbi__err("not GIF"); 75 | if (stbi__get8() != 'a') 76 | stbi__err("not GIF"); 77 | w = stbi__get16le(); 78 | h = stbi__get16le(); 79 | flags = stbi__get8(); 80 | bgindex = stbi__get8(); 81 | ratio = stbi__get8(); 82 | transparent = -1; 83 | 84 | comp = 4; 85 | if (is_info != 0) 86 | return 1; 87 | if ((flags & 0x80) != 0) 88 | stbi__gif_parse_colortable(pal, 2 << (flags & 7), -1); 89 | return 1; 90 | } 91 | 92 | private void stbi__out_gif_code(ushort code) 93 | { 94 | var idx = 0; 95 | if (codes[code].prefix >= 0) 96 | stbi__out_gif_code((ushort)codes[code].prefix); 97 | if (cur_y >= max_y) 98 | return; 99 | idx = cur_x + cur_y; 100 | history[idx / 4] = 1; 101 | var c = new FakePtr(color_table, codes[code].suffix * 4); 102 | if (c[3] > 128) 103 | { 104 | var p = new FakePtr(_out_, idx); 105 | p[0] = c[2]; 106 | p[1] = c[1]; 107 | p[2] = c[0]; 108 | p[3] = c[3]; 109 | } 110 | 111 | cur_x += 4; 112 | if (cur_x >= max_x) 113 | { 114 | cur_x = start_x; 115 | cur_y += step; 116 | while (cur_y >= max_y && parse > 0) 117 | { 118 | step = (1 << parse) * line_size; 119 | cur_y = start_y + (step >> 1); 120 | --parse; 121 | } 122 | } 123 | } 124 | 125 | private byte[] stbi__process_gif_raster() 126 | { 127 | byte lzw_cs = 0; 128 | var len = 0; 129 | var init_code = 0; 130 | uint first = 0; 131 | var codesize = 0; 132 | var codemask = 0; 133 | var avail = 0; 134 | var oldcode = 0; 135 | var bits = 0; 136 | var valid_bits = 0; 137 | var clear = 0; 138 | lzw_cs = stbi__get8(); 139 | if (lzw_cs > 12) 140 | return null; 141 | clear = 1 << lzw_cs; 142 | first = 1; 143 | codesize = lzw_cs + 1; 144 | codemask = (1 << codesize) - 1; 145 | bits = 0; 146 | valid_bits = 0; 147 | for (init_code = 0; init_code < clear; init_code++) 148 | { 149 | codes[init_code].prefix = -1; 150 | codes[init_code].first = (byte)init_code; 151 | codes[init_code].suffix = (byte)init_code; 152 | } 153 | 154 | avail = clear + 2; 155 | oldcode = -1; 156 | len = 0; 157 | for (; ; ) 158 | if (valid_bits < codesize) 159 | { 160 | if (len == 0) 161 | { 162 | len = stbi__get8(); 163 | if (len == 0) 164 | return _out_; 165 | } 166 | 167 | --len; 168 | bits |= stbi__get8() << valid_bits; 169 | valid_bits += 8; 170 | } 171 | else 172 | { 173 | var code = bits & codemask; 174 | bits >>= codesize; 175 | valid_bits -= codesize; 176 | if (code == clear) 177 | { 178 | codesize = lzw_cs + 1; 179 | codemask = (1 << codesize) - 1; 180 | avail = clear + 2; 181 | oldcode = -1; 182 | first = 0; 183 | } 184 | else if (code == clear + 1) 185 | { 186 | stbi__skip(len); 187 | while ((len = stbi__get8()) > 0) stbi__skip(len); 188 | return _out_; 189 | } 190 | else if (code <= avail) 191 | { 192 | if (first != 0) stbi__err("no clear code"); 193 | if (oldcode >= 0) 194 | { 195 | var idx = avail++; 196 | if (avail > 8192) stbi__err("too many codes"); 197 | codes[idx].prefix = (short)oldcode; 198 | codes[idx].first = codes[oldcode].first; 199 | codes[idx].suffix = code == avail ? codes[idx].first : codes[code].first; 200 | } 201 | else if (code == avail) 202 | { 203 | stbi__err("illegal code in raster"); 204 | } 205 | 206 | stbi__out_gif_code((ushort)code); 207 | if ((avail & codemask) == 0 && avail <= 0x0FFF) 208 | { 209 | codesize++; 210 | codemask = (1 << codesize) - 1; 211 | } 212 | 213 | oldcode = code; 214 | } 215 | else 216 | { 217 | stbi__err("illegal code in raster"); 218 | } 219 | } 220 | } 221 | 222 | private byte[] stbi__gif_load_next(out int comp, FakePtr? two_back) 223 | { 224 | comp = 0; 225 | 226 | var dispose = 0; 227 | var first_frame = 0; 228 | var pi = 0; 229 | var pcount = 0; 230 | first_frame = 0; 231 | if (_out_ == null) 232 | { 233 | if (stbi__gif_header(out comp, 0) == 0) 234 | return null; 235 | pcount = w * h; 236 | _out_ = new byte[4 * pcount]; 237 | Array.Clear(_out_, 0, _out_.Length); 238 | background = new byte[4 * pcount]; 239 | Array.Clear(background, 0, background.Length); 240 | history = new byte[pcount]; 241 | Array.Clear(history, 0, history.Length); 242 | first_frame = 1; 243 | } 244 | else 245 | { 246 | var ptr = new FakePtr(_out_); 247 | dispose = (eflags & 0x1C) >> 2; 248 | pcount = w * h; 249 | if (dispose == 3 && two_back == null) dispose = 2; 250 | if (dispose == 3) 251 | { 252 | for (pi = 0; pi < pcount; ++pi) 253 | if (history[pi] != 0) 254 | new FakePtr(ptr, pi * 4).memcpy(new FakePtr(two_back.Value, pi * 4), 4); 255 | } 256 | else if (dispose == 2) 257 | { 258 | for (pi = 0; pi < pcount; ++pi) 259 | if (history[pi] != 0) 260 | new FakePtr(ptr, pi * 4).memcpy(new FakePtr(background, pi * 4), 4); 261 | } 262 | 263 | new FakePtr(background).memcpy(ptr, 4 * w * h); 264 | } 265 | 266 | Array.Clear(history, 0, w * h); 267 | for (; ; ) 268 | { 269 | var tag = (int)stbi__get8(); 270 | switch (tag) 271 | { 272 | case 0x2C: 273 | { 274 | var x = 0; 275 | var y = 0; 276 | var w = 0; 277 | var h = 0; 278 | byte[] o; 279 | x = stbi__get16le(); 280 | y = stbi__get16le(); 281 | w = stbi__get16le(); 282 | h = stbi__get16le(); 283 | if (x + w > w || y + h > h) 284 | stbi__err("bad Image Descriptor"); 285 | line_size = w * 4; 286 | start_x = x * 4; 287 | start_y = y * line_size; 288 | max_x = start_x + w * 4; 289 | max_y = start_y + h * line_size; 290 | cur_x = start_x; 291 | cur_y = start_y; 292 | if (w == 0) 293 | cur_y = max_y; 294 | lflags = stbi__get8(); 295 | if ((lflags & 0x40) != 0) 296 | { 297 | step = 8 * line_size; 298 | parse = 3; 299 | } 300 | else 301 | { 302 | step = line_size; 303 | parse = 0; 304 | } 305 | 306 | if ((lflags & 0x80) != 0) 307 | { 308 | stbi__gif_parse_colortable(lpal, 2 << (lflags & 7), 309 | (eflags & 0x01) != 0 ? transparent : -1); 310 | color_table = lpal; 311 | } 312 | else if ((flags & 0x80) != 0) 313 | { 314 | color_table = pal; 315 | } 316 | else 317 | { 318 | stbi__err("missing color table"); 319 | } 320 | 321 | o = stbi__process_gif_raster(); 322 | if (o == null) 323 | return null; 324 | pcount = w * h; 325 | if (first_frame != 0 && bgindex > 0) 326 | for (pi = 0; pi < pcount; ++pi) 327 | if (history[pi] == 0) 328 | { 329 | pal[bgindex * 4 + 3] = 255; 330 | new FakePtr(_out_, pi * 4).memcpy(new FakePtr(pal, bgindex), 4); 331 | } 332 | 333 | return o; 334 | } 335 | case 0x21: 336 | { 337 | var len = 0; 338 | var ext = (int)stbi__get8(); 339 | if (ext == 0xF9) 340 | { 341 | len = stbi__get8(); 342 | if (len == 4) 343 | { 344 | eflags = stbi__get8(); 345 | delay = 10 * stbi__get16le(); 346 | if (transparent >= 0) pal[transparent * 4 + 3] = 255; 347 | if ((eflags & 0x01) != 0) 348 | { 349 | transparent = stbi__get8(); 350 | if (transparent >= 0) pal[transparent * 4 + 3] = 0; 351 | } 352 | else 353 | { 354 | stbi__skip(1); 355 | transparent = -1; 356 | } 357 | } 358 | else 359 | { 360 | stbi__skip(len); 361 | break; 362 | } 363 | } 364 | 365 | while ((len = stbi__get8()) != 0) stbi__skip(len); 366 | break; 367 | } 368 | case 0x3B: 369 | return null; 370 | default: 371 | stbi__err("unknown code"); 372 | break; 373 | } 374 | } 375 | } 376 | 377 | /* private void* stbi__load_gif_main(int** delays, int* x, int* y, int* z, int* comp, int req_comp) 378 | { 379 | if ((IsGif(Stream))) 380 | { 381 | int layers = (int)(0); 382 | byte* u = null; 383 | byte* _out_ = null; 384 | byte* two_back = null; 385 | int stride = 0; 386 | if ((delays) != null) 387 | { 388 | *delays = null; 389 | } 390 | do 391 | { 392 | u = stbi__gif_load_next(comp, (int)(req_comp), two_back); 393 | if ((u) != null) 394 | { 395 | *x = (int)(w); 396 | *y = (int)(h); 397 | ++layers; 398 | stride = (int)(w * h * 4); 399 | if ((_out_) != null) 400 | { 401 | _out_ = (byte*)(CRuntime.realloc(_out_, (ulong)(layers * stride))); 402 | if ((delays) != null) 403 | { 404 | *delays = (int*)(CRuntime.realloc(*delays, (ulong)(sizeof(int) * layers))); 405 | } 406 | } 407 | else 408 | { 409 | _out_ = (byte*)(Utility.stbi__malloc((ulong)(layers * stride))); 410 | if ((delays) != null) 411 | { 412 | *delays = (int*)(Utility.stbi__malloc((ulong)(layers * sizeof(int)))); 413 | } 414 | } 415 | CRuntime.memcpy(_out_ + ((layers - 1) * stride), u, (ulong)(stride)); 416 | if ((layers) >= (2)) 417 | { 418 | two_back = _out_ - 2 * stride; 419 | } 420 | if ((delays) != null) 421 | { 422 | (*delays)[layers - 1U] = (int)(delay); 423 | } 424 | } 425 | } 426 | while (u != null); 427 | CRuntime.free(_out_); 428 | CRuntime.free(history); 429 | CRuntime.free(background); 430 | if (((req_comp) != 0) && (req_comp != 4)) 431 | _out_ = stbi__convert_format(_out_, (int)(4), (int)(req_comp), (uint)(layers * w), (uint)(h)); 432 | *z = (int)(layers); 433 | return _out_; 434 | } 435 | else 436 | { 437 | stbi__err("not GIF"); 438 | } 439 | 440 | }*/ 441 | 442 | private ImageResult InternalDecode(ColorComponents? requiredComponents) 443 | { 444 | int comp; 445 | var u = stbi__gif_load_next(out comp, null); 446 | if (u == null) throw new Exception("could not decode gif"); 447 | 448 | if (requiredComponents != null && requiredComponents.Value != ColorComponents.RedGreenBlueAlpha) 449 | u = Conversion.stbi__convert_format(u, 4, (int)requiredComponents.Value, (uint)w, (uint)h); 450 | 451 | return new ImageResult 452 | { 453 | Width = w, 454 | Height = h, 455 | SourceComponents = (ColorComponents)comp, 456 | ColorComponents = requiredComponents != null ? requiredComponents.Value : (ColorComponents)comp, 457 | BitsPerChannel = 8, 458 | Data = u 459 | }; 460 | } 461 | 462 | private static bool InternalTest(Stream stream) 463 | { 464 | var sz = 0; 465 | if (stream.stbi__get8() != 'G' || stream.stbi__get8() != 'I' || stream.stbi__get8() != 'F' || 466 | stream.stbi__get8() != '8') 467 | return false; 468 | sz = stream.stbi__get8(); 469 | if (sz != '9' && sz != '7') 470 | return false; 471 | if (stream.stbi__get8() != 'a') 472 | return false; 473 | return true; 474 | } 475 | 476 | public static bool Test(Stream stream) 477 | { 478 | var result = InternalTest(stream); 479 | stream.Rewind(); 480 | return result; 481 | } 482 | 483 | public static ImageInfo? Info(Stream stream) 484 | { 485 | var decoder = new GifDecoder(stream); 486 | 487 | int comp; 488 | var r = decoder.stbi__gif_header(out comp, 1); 489 | stream.Rewind(); 490 | if (r == 0) return null; 491 | 492 | return new ImageInfo 493 | { 494 | Width = decoder.w, 495 | Height = decoder.h, 496 | ColorComponents = (ColorComponents)comp, 497 | BitsPerChannel = 8 498 | }; 499 | } 500 | 501 | public static ImageResult Decode(Stream stream, ColorComponents? requiredComponents = null) 502 | { 503 | var decoder = new GifDecoder(stream); 504 | return decoder.InternalDecode(requiredComponents); 505 | } 506 | } 507 | } -------------------------------------------------------------------------------- /src/Decoding/PngDecoder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using StbImageSharp.Utility; 5 | 6 | namespace StbImageSharp.Decoding 7 | { 8 | #if !STBSHARP_INTERNAL 9 | public 10 | #else 11 | internal 12 | #endif 13 | class PngDecoder : Decoder 14 | { 15 | [StructLayout(LayoutKind.Sequential)] 16 | public struct stbi__pngchunk 17 | { 18 | public uint length; 19 | public uint type; 20 | } 21 | 22 | private const int STBI__F_none = 0; 23 | private const int STBI__F_sub = 1; 24 | private const int STBI__F_up = 2; 25 | private const int STBI__F_avg = 3; 26 | private const int STBI__F_paeth = 4; 27 | private const int STBI__F_avg_first = 5; 28 | private const int STBI__F_paeth_first = 6; 29 | 30 | private static readonly byte[] first_row_filter = 31 | {STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first}; 32 | 33 | private static readonly byte[] stbi__depth_scale_table = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; 34 | private static readonly byte[] png_sig = { 137, 80, 78, 71, 13, 10, 26, 10 }; 35 | 36 | protected int img_out_n; 37 | 38 | private int stbi__unpremultiply_on_load; 39 | 40 | private int stbi__de_iphone_flag; 41 | 42 | private byte[] idata; 43 | private byte[] expanded; 44 | private byte[] _out_; 45 | private int depth; 46 | 47 | private PngDecoder(Stream stream) : base(stream) 48 | { 49 | } 50 | 51 | private stbi__pngchunk stbi__get_chunk_header() 52 | { 53 | var c = new stbi__pngchunk(); 54 | c.length = stbi__get32be(); 55 | c.type = stbi__get32be(); 56 | return c; 57 | } 58 | 59 | private static bool stbi__check_png_header(Stream input) 60 | { 61 | var i = 0; 62 | for (i = 0; i < 8; ++i) 63 | if (input.ReadByte() != png_sig[i]) 64 | return false; 65 | 66 | return true; 67 | } 68 | 69 | private static int stbi__paeth(int a, int b, int c) 70 | { 71 | var p = a + b - c; 72 | var pa = Math.Abs(p - a); 73 | var pb = Math.Abs(p - b); 74 | var pc = Math.Abs(p - c); 75 | if (pa <= pb && pa <= pc) 76 | return a; 77 | if (pb <= pc) 78 | return b; 79 | return c; 80 | } 81 | 82 | private int stbi__create_png_image_raw(FakePtr raw, uint raw_len, int out_n, uint x, uint y, int depth, 83 | int color) 84 | { 85 | var bytes = depth == 16 ? 2 : 1; 86 | uint i = 0; 87 | uint j = 0; 88 | var stride = (uint)(x * out_n * bytes); 89 | uint img_len = 0; 90 | uint img_width_bytes = 0; 91 | var k = 0; 92 | var output_bytes = out_n * bytes; 93 | var filter_bytes = img_n * bytes; 94 | var width = (int)x; 95 | _out_ = new byte[x * y * output_bytes]; 96 | img_width_bytes = (uint)((img_n * x * depth + 7) >> 3); 97 | img_len = (img_width_bytes + 1) * y; 98 | if (raw_len < img_len) 99 | stbi__err("not enough pixels"); 100 | var ptr = new FakePtr(_out_); 101 | for (j = (uint)0; j < y; ++j) 102 | { 103 | var cur = ptr + stride * j; 104 | FakePtr prior; 105 | var filter = (int)raw.Value; 106 | raw++; 107 | if (filter > 4) 108 | stbi__err("invalid filter"); 109 | if (depth < 8) 110 | { 111 | cur += x * out_n - img_width_bytes; 112 | filter_bytes = 1; 113 | width = (int)img_width_bytes; 114 | } 115 | 116 | prior = cur - stride; 117 | if (j == 0) 118 | filter = first_row_filter[filter]; 119 | for (k = 0; k < filter_bytes; ++k) 120 | switch (filter) 121 | { 122 | case STBI__F_none: 123 | cur[k] = raw[k]; 124 | break; 125 | case STBI__F_sub: 126 | cur[k] = raw[k]; 127 | break; 128 | case STBI__F_up: 129 | cur[k] = (byte)((raw[k] + prior[k]) & 255); 130 | break; 131 | case STBI__F_avg: 132 | cur[k] = (byte)((raw[k] + (prior[k] >> 1)) & 255); 133 | break; 134 | case STBI__F_paeth: 135 | cur[k] = (byte)((raw[k] + stbi__paeth(0, prior[k], 0)) & 255); 136 | break; 137 | case STBI__F_avg_first: 138 | cur[k] = raw[k]; 139 | break; 140 | case STBI__F_paeth_first: 141 | cur[k] = raw[k]; 142 | break; 143 | } 144 | 145 | if (depth == 8) 146 | { 147 | if (img_n != out_n) 148 | cur[img_n] = 255; 149 | raw += img_n; 150 | cur += out_n; 151 | prior += out_n; 152 | } 153 | else if (depth == 16) 154 | { 155 | if (img_n != out_n) 156 | { 157 | cur[filter_bytes] = 255; 158 | cur[filter_bytes + 1] = 255; 159 | } 160 | 161 | raw += filter_bytes; 162 | cur += output_bytes; 163 | prior += output_bytes; 164 | } 165 | else 166 | { 167 | raw += 1; 168 | cur += 1; 169 | prior += 1; 170 | } 171 | 172 | if (depth < 8 || img_n == out_n) 173 | { 174 | var nk = (width - 1) * filter_bytes; 175 | switch (filter) 176 | { 177 | case STBI__F_none: 178 | cur.memcpy(raw, nk); 179 | break; 180 | case STBI__F_sub: 181 | for (k = 0; k < nk; ++k) cur[k] = (byte)((raw[k] + cur[k - filter_bytes]) & 255); 182 | break; 183 | case STBI__F_up: 184 | for (k = 0; k < nk; ++k) cur[k] = (byte)((raw[k] + prior[k]) & 255); 185 | break; 186 | case STBI__F_avg: 187 | for (k = 0; k < nk; ++k) 188 | cur[k] = (byte)((raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)) & 255); 189 | break; 190 | case STBI__F_paeth: 191 | for (k = 0; k < nk; ++k) 192 | cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], 193 | prior[k - filter_bytes])) & 255); 194 | break; 195 | case STBI__F_avg_first: 196 | for (k = 0; k < nk; ++k) cur[k] = (byte)((raw[k] + (cur[k - filter_bytes] >> 1)) & 255); 197 | break; 198 | case STBI__F_paeth_first: 199 | for (k = 0; k < nk; ++k) 200 | cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - filter_bytes], 0, 0)) & 255); 201 | break; 202 | } 203 | 204 | raw += nk; 205 | } 206 | else 207 | { 208 | switch (filter) 209 | { 210 | case STBI__F_none: 211 | for (i = x - 1; 212 | i >= 1; 213 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 214 | output_bytes) 215 | for (k = 0; k < filter_bytes; ++k) 216 | cur[k] = raw[k]; 217 | break; 218 | case STBI__F_sub: 219 | for (i = x - 1; 220 | i >= 1; 221 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 222 | output_bytes) 223 | for (k = 0; k < filter_bytes; ++k) 224 | cur[k] = (byte)((raw[k] + cur[k - output_bytes]) & 255); 225 | break; 226 | case STBI__F_up: 227 | for (i = x - 1; 228 | i >= 1; 229 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 230 | output_bytes) 231 | for (k = 0; k < filter_bytes; ++k) 232 | cur[k] = (byte)((raw[k] + prior[k]) & 255); 233 | break; 234 | case STBI__F_avg: 235 | for (i = x - 1; 236 | i >= 1; 237 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 238 | output_bytes) 239 | for (k = 0; k < filter_bytes; ++k) 240 | cur[k] = (byte)((raw[k] + ((prior[k] + cur[k - output_bytes]) >> 1)) & 255); 241 | break; 242 | case STBI__F_paeth: 243 | for (i = x - 1; 244 | i >= 1; 245 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 246 | output_bytes) 247 | for (k = 0; k < filter_bytes; ++k) 248 | cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - output_bytes], prior[k], 249 | prior[k - output_bytes])) & 255); 250 | break; 251 | case STBI__F_avg_first: 252 | for (i = x - 1; 253 | i >= 1; 254 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 255 | output_bytes) 256 | for (k = 0; k < filter_bytes; ++k) 257 | cur[k] = (byte)((raw[k] + (cur[k - output_bytes] >> 1)) & 255); 258 | break; 259 | case STBI__F_paeth_first: 260 | for (i = x - 1; 261 | i >= 1; 262 | --i, cur[filter_bytes] = (byte)255, raw += filter_bytes, cur += output_bytes, prior += 263 | output_bytes) 264 | for (k = 0; k < filter_bytes; ++k) 265 | cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - output_bytes], 0, 0)) & 255); 266 | break; 267 | } 268 | 269 | if (depth == 16) 270 | { 271 | cur = ptr + stride * j; 272 | for (i = (uint)0; i < x; ++i, cur += output_bytes) cur[filter_bytes + 1] = 255; 273 | } 274 | } 275 | } 276 | 277 | if (depth < 8) 278 | for (j = (uint)0; j < y; ++j) 279 | { 280 | var cur = ptr + stride * j; 281 | var _in_ = ptr + stride * j + x * out_n - img_width_bytes; 282 | var scale = (byte)(color == 0 ? stbi__depth_scale_table[depth] : 1); 283 | if (depth == 4) 284 | { 285 | for (k = (int)(x * img_n); k >= 2; k -= 2, ++_in_) 286 | { 287 | cur.SetAndIncrease((byte)(scale * (_in_.Value >> 4))); 288 | cur.SetAndIncrease((byte)(scale * (_in_.Value & 0x0f))); 289 | } 290 | 291 | if (k > 0) 292 | cur.SetAndIncrease((byte)(scale * (_in_.Value >> 4))); 293 | } 294 | else if (depth == 2) 295 | { 296 | for (k = (int)(x * img_n); k >= 4; k -= 4, ++_in_) 297 | { 298 | cur.SetAndIncrease((byte)(scale * (_in_.Value >> 6))); 299 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 4) & 0x03))); 300 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 2) & 0x03))); 301 | cur.SetAndIncrease((byte)(scale * (_in_.Value & 0x03))); 302 | } 303 | 304 | if (k > 0) 305 | cur.SetAndIncrease((byte)(scale * (_in_.Value >> 6))); 306 | if (k > 1) 307 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 4) & 0x03))); 308 | if (k > 2) 309 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 2) & 0x03))); 310 | } 311 | else if (depth == 1) 312 | { 313 | for (k = (int)(x * img_n); k >= 8; k -= 8, ++_in_) 314 | { 315 | cur.SetAndIncrease((byte)(scale * (_in_.Value >> 7))); 316 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 6) & 0x01))); 317 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 5) & 0x01))); 318 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 4) & 0x01))); 319 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 3) & 0x01))); 320 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 2) & 0x01))); 321 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 1) & 0x01))); 322 | cur.SetAndIncrease((byte)(scale * (_in_.Value & 0x01))); 323 | } 324 | 325 | if (k > 0) 326 | cur.SetAndIncrease((byte)(scale * (_in_.Value >> 7))); 327 | if (k > 1) 328 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 6) & 0x01))); 329 | if (k > 2) 330 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 5) & 0x01))); 331 | if (k > 3) 332 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 4) & 0x01))); 333 | if (k > 4) 334 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 3) & 0x01))); 335 | if (k > 5) 336 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 2) & 0x01))); 337 | if (k > 6) 338 | cur.SetAndIncrease((byte)(scale * ((_in_.Value >> 1) & 0x01))); 339 | } 340 | 341 | if (img_n != out_n) 342 | { 343 | var q = 0; 344 | cur = ptr + stride * j; 345 | if (img_n == 1) 346 | for (q = (int)(x - 1); q >= 0; --q) 347 | { 348 | cur[q * 2 + 1] = 255; 349 | cur[q * 2 + 0] = cur[q]; 350 | } 351 | else 352 | for (q = (int)(x - 1); q >= 0; --q) 353 | { 354 | cur[q * 4 + 3] = 255; 355 | cur[q * 4 + 2] = cur[q * 3 + 2]; 356 | cur[q * 4 + 1] = cur[q * 3 + 1]; 357 | cur[q * 4 + 0] = cur[q * 3 + 0]; 358 | } 359 | } 360 | } 361 | else if (depth == 16) 362 | throw new NotImplementedException(); 363 | /* FakePtr cur = ptr; 364 | ushort* cur16 = (ushort*)(cur); 365 | for (i = (uint)(0); (i) < (x * y * out_n); ++i, cur16++, cur += 2) 366 | { 367 | *cur16 = (ushort)((cur[0] << 8) | cur[1]); 368 | }*/ 369 | 370 | return 1; 371 | } 372 | 373 | private int stbi__create_png_image(FakePtr image_data, uint image_data_len, int out_n, int depth, 374 | int color, int interlaced) 375 | { 376 | var bytes = depth == 16 ? 2 : 1; 377 | var out_bytes = out_n * bytes; 378 | var p = 0; 379 | if (interlaced == 0) 380 | return stbi__create_png_image_raw(image_data, image_data_len, out_n, (uint)img_x, (uint)img_y, depth, 381 | color); 382 | var final = new byte[img_x * img_y * out_bytes]; 383 | var xorig = new int[7]; 384 | var yorig = new int[7]; 385 | var xspc = new int[7]; 386 | var yspc = new int[7]; 387 | 388 | for (p = 0; p < 7; ++p) 389 | { 390 | xorig[0] = 0; 391 | xorig[1] = 4; 392 | xorig[2] = 0; 393 | xorig[3] = 2; 394 | xorig[4] = 0; 395 | xorig[5] = 1; 396 | xorig[6] = 0; 397 | 398 | yorig[0] = 0; 399 | yorig[1] = 0; 400 | yorig[2] = 4; 401 | yorig[3] = 0; 402 | yorig[4] = 2; 403 | yorig[5] = 0; 404 | yorig[6] = 1; 405 | 406 | xspc[0] = 8; 407 | xspc[1] = 8; 408 | xspc[2] = 4; 409 | xspc[3] = 4; 410 | xspc[4] = 2; 411 | xspc[5] = 2; 412 | xspc[6] = 1; 413 | 414 | yspc[0] = 8; 415 | yspc[1] = 8; 416 | yspc[2] = 8; 417 | yspc[3] = 4; 418 | yspc[4] = 4; 419 | yspc[5] = 2; 420 | yspc[6] = 2; 421 | var i = 0; 422 | var j = 0; 423 | var x = 0; 424 | var y = 0; 425 | x = (img_x - xorig[p] + xspc[p] - 1) / xspc[p]; 426 | y = (img_y - yorig[p] + yspc[p] - 1) / yspc[p]; 427 | if (x != 0 && y != 0) 428 | { 429 | var img_len = (uint)((((img_n * x * depth + 7) >> 3) + 1) * y); 430 | if (stbi__create_png_image_raw(image_data, image_data_len, out_n, (uint)x, (uint)y, depth, 431 | color) == 0) return 0; 432 | 433 | var finalPtr = new FakePtr(final); 434 | var outPtr = new FakePtr(_out_); 435 | for (j = 0; j < y; ++j) 436 | for (i = 0; i < x; ++i) 437 | { 438 | var out_y = j * yspc[p] + yorig[p]; 439 | var out_x = i * xspc[p] + xorig[p]; 440 | (finalPtr + out_y * img_x * out_bytes + out_x * out_bytes).memcpy( 441 | outPtr + (j * x + i) * out_bytes, 442 | out_bytes); 443 | } 444 | 445 | image_data += img_len; 446 | image_data_len -= img_len; 447 | } 448 | } 449 | 450 | _out_ = final; 451 | return 1; 452 | } 453 | 454 | private int stbi__compute_transparency(byte[] tc, int out_n) 455 | { 456 | uint i = 0; 457 | var pixel_count = (uint)(img_x * img_y); 458 | var p = new FakePtr(_out_); 459 | if (out_n == 2) 460 | for (i = (uint)0; i < pixel_count; ++i) 461 | { 462 | p[1] = (byte)(p[0] == tc[0] ? 0 : 255); 463 | p += 2; 464 | } 465 | else 466 | for (i = (uint)0; i < pixel_count; ++i) 467 | { 468 | if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) 469 | p[3] = 0; 470 | p += 4; 471 | } 472 | 473 | return 1; 474 | } 475 | 476 | private int stbi__compute_transparency16(ushort[] tc, int out_n) 477 | { 478 | throw new NotImplementedException(); 479 | 480 | /* uint i = 0; 481 | uint pixel_count = (uint)(img_x * img_y); 482 | FakePtr p = new FakePtr(_out_); 483 | if ((out_n) == (2)) 484 | { 485 | for (i = (uint)(0); (i) < (pixel_count); ++i) 486 | { 487 | p[1] = (ushort)((p[0]) == (tc[0]) ? 0 : 65535); 488 | p += 2; 489 | } 490 | } 491 | else 492 | { 493 | for (i = (uint)(0); (i) < (pixel_count); ++i) 494 | { 495 | if ((((p[0]) == (tc[0])) && ((p[1]) == (tc[1]))) && ((p[2]) == (tc[2]))) 496 | p[3] = (ushort)(0); 497 | p += 4; 498 | } 499 | } 500 | 501 | return (int)(1);*/ 502 | } 503 | 504 | private int stbi__expand_png_palette(byte[] palette, int len, int pal_img_n) 505 | { 506 | uint i = 0; 507 | var pixel_count = (uint)(img_x * img_y); 508 | var orig = _out_; 509 | _out_ = new byte[pixel_count * pal_img_n]; 510 | var p = new FakePtr(_out_); 511 | if (pal_img_n == 3) 512 | for (i = (uint)0; i < pixel_count; ++i) 513 | { 514 | var n = orig[i] * 4; 515 | p[0] = palette[n]; 516 | p[1] = palette[n + 1]; 517 | p[2] = palette[n + 2]; 518 | p += 3; 519 | } 520 | else 521 | for (i = (uint)0; i < pixel_count; ++i) 522 | { 523 | var n = orig[i] * 4; 524 | p[0] = palette[n]; 525 | p[1] = palette[n + 1]; 526 | p[2] = palette[n + 2]; 527 | p[3] = palette[n + 3]; 528 | p += 4; 529 | } 530 | 531 | return 1; 532 | } 533 | 534 | private void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) 535 | { 536 | stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; 537 | } 538 | 539 | private void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) 540 | { 541 | stbi__de_iphone_flag = flag_true_if_should_convert; 542 | } 543 | 544 | private void stbi__de_iphone() 545 | { 546 | uint i = 0; 547 | var pixel_count = (uint)(img_x * img_y); 548 | var p = new FakePtr(_out_); 549 | if (img_out_n == 3) 550 | { 551 | for (i = (uint)0; i < pixel_count; ++i) 552 | { 553 | var t = p[0]; 554 | p[0] = p[2]; 555 | p[2] = t; 556 | p += 3; 557 | } 558 | } 559 | else 560 | { 561 | if (stbi__unpremultiply_on_load != 0) 562 | for (i = (uint)0; i < pixel_count; ++i) 563 | { 564 | var a = p[3]; 565 | var t = p[0]; 566 | if (a != 0) 567 | { 568 | var half = (byte)(a / 2); 569 | p[0] = (byte)((p[2] * 255 + half) / a); 570 | p[1] = (byte)((p[1] * 255 + half) / a); 571 | p[2] = (byte)((t * 255 + half) / a); 572 | } 573 | else 574 | { 575 | p[0] = p[2]; 576 | p[2] = t; 577 | } 578 | 579 | p += 4; 580 | } 581 | else 582 | for (i = (uint)0; i < pixel_count; ++i) 583 | { 584 | var t = p[0]; 585 | p[0] = p[2]; 586 | p[2] = t; 587 | p += 4; 588 | } 589 | } 590 | } 591 | 592 | private int stbi__parse_png_file(int scan, int req_comp) 593 | { 594 | var palette = new byte[1024]; 595 | var pal_img_n = (byte)0; 596 | var has_trans = (byte)0; 597 | var tc = new byte[3]; 598 | tc[0] = 0; 599 | 600 | var tc16 = new ushort[3]; 601 | var ioff = 0; 602 | var idata_limit = 0; 603 | uint i = 0; 604 | var pal_len = (uint)0; 605 | var first = 1; 606 | var k = 0; 607 | var interlace = 0; 608 | var color = 0; 609 | var is_iphone = 0; 610 | expanded = null; 611 | idata = null; 612 | _out_ = null; 613 | if (!stbi__check_png_header(Stream)) 614 | return 0; 615 | if (scan == STBI__SCAN_type) 616 | return 1; 617 | for (; ; ) 618 | { 619 | var c = stbi__get_chunk_header(); 620 | switch (c.type) 621 | { 622 | case ((uint)'C' << 24) + ((uint)'g' << 16) + ((uint)'B' << 8) + 'I': 623 | is_iphone = 1; 624 | stbi__skip((int)c.length); 625 | break; 626 | case ((uint)'I' << 24) + ((uint)'H' << 16) + ((uint)'D' << 8) + 'R': 627 | { 628 | var comp = 0; 629 | var filter = 0; 630 | if (first == 0) 631 | stbi__err("multiple IHDR"); 632 | first = 0; 633 | if (c.length != 13) 634 | stbi__err("bad IHDR len"); 635 | img_x = (int)stbi__get32be(); 636 | if (img_x > 1 << 24) 637 | stbi__err("too large"); 638 | img_y = (int)stbi__get32be(); 639 | if (img_y > 1 << 24) 640 | stbi__err("too large"); 641 | depth = stbi__get8(); 642 | if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && depth != 16) 643 | stbi__err("1/2/4/8/16-bit only"); 644 | color = stbi__get8(); 645 | if (color > 6) 646 | stbi__err("bad ctype"); 647 | if (color == 3 && depth == 16) 648 | stbi__err("bad ctype"); 649 | if (color == 3) 650 | pal_img_n = 3; 651 | else if ((color & 1) != 0) 652 | stbi__err("bad ctype"); 653 | comp = stbi__get8(); 654 | if (comp != 0) 655 | stbi__err("bad comp method"); 656 | filter = stbi__get8(); 657 | if (filter != 0) 658 | stbi__err("bad filter method"); 659 | interlace = stbi__get8(); 660 | if (interlace > 1) 661 | stbi__err("bad interlace method"); 662 | if (img_x == 0 || img_y == 0) 663 | stbi__err("0-pixel image"); 664 | if (pal_img_n == 0) 665 | { 666 | img_n = ((color & 2) != 0 ? 3 : 1) + ((color & 4) != 0 ? 1 : 0); 667 | if ((1 << 30) / img_x / img_n < img_y) 668 | stbi__err("too large"); 669 | if (scan == STBI__SCAN_header) 670 | return 1; 671 | } 672 | else 673 | { 674 | img_n = 1; 675 | if ((1 << 30) / img_x / 4 < img_y) 676 | stbi__err("too large"); 677 | } 678 | 679 | break; 680 | } 681 | case ((uint)'P' << 24) + ((uint)'L' << 16) + ((uint)'T' << 8) + 'E': 682 | { 683 | if (first != 0) 684 | stbi__err("first not IHDR"); 685 | if (c.length > 256 * 3) 686 | stbi__err("invalid PLTE"); 687 | pal_len = c.length / 3; 688 | if (pal_len * 3 != c.length) 689 | stbi__err("invalid PLTE"); 690 | for (i = (uint)0; i < pal_len; ++i) 691 | { 692 | palette[i * 4 + 0] = stbi__get8(); 693 | palette[i * 4 + 1] = stbi__get8(); 694 | palette[i * 4 + 2] = stbi__get8(); 695 | palette[i * 4 + 3] = 255; 696 | } 697 | 698 | break; 699 | } 700 | case ((uint)'t' << 24) + ((uint)'R' << 16) + ((uint)'N' << 8) + 'S': 701 | { 702 | if (first != 0) 703 | stbi__err("first not IHDR"); 704 | if (idata != null) 705 | stbi__err("tRNS after IDAT"); 706 | if (pal_img_n != 0) 707 | { 708 | if (scan == STBI__SCAN_header) 709 | { 710 | img_n = 4; 711 | return 1; 712 | } 713 | 714 | if (pal_len == 0) 715 | stbi__err("tRNS before PLTE"); 716 | if (c.length > pal_len) 717 | stbi__err("bad tRNS len"); 718 | pal_img_n = 4; 719 | for (i = (uint)0; i < c.length; ++i) palette[i * 4 + 3] = stbi__get8(); 720 | } 721 | else 722 | { 723 | if ((img_n & 1) == 0) 724 | stbi__err("tRNS with alpha"); 725 | if (c.length != (uint)img_n * 2) 726 | stbi__err("bad tRNS len"); 727 | has_trans = 1; 728 | if (depth == 16) 729 | for (k = 0; k < img_n; ++k) 730 | tc16[k] = (ushort)stbi__get16be(); 731 | else 732 | for (k = 0; k < img_n; ++k) 733 | tc[k] = (byte)((byte)(stbi__get16be() & 255) * stbi__depth_scale_table[depth]); 734 | } 735 | 736 | break; 737 | } 738 | case ((uint)'I' << 24) + ((uint)'D' << 16) + ((uint)'A' << 8) + 'T': 739 | { 740 | if (first != 0) 741 | stbi__err("first not IHDR"); 742 | if (pal_img_n != 0 && pal_len == 0) 743 | stbi__err("no PLTE"); 744 | if (scan == STBI__SCAN_header) 745 | { 746 | img_n = pal_img_n; 747 | return 1; 748 | } 749 | 750 | if ((int)(ioff + c.length) < ioff) 751 | return 0; 752 | if (ioff + c.length > idata_limit) 753 | { 754 | var idata_limit_old = (uint)idata_limit; 755 | if (idata_limit == 0) 756 | idata_limit = (int)(c.length > 4096 ? c.length : 4096); 757 | while (ioff + c.length > idata_limit) idata_limit *= 2; 758 | 759 | Array.Resize(ref idata, idata_limit); 760 | } 761 | 762 | if (!stbi__getn(idata, ioff, (int)c.length)) 763 | stbi__err("outofdata"); 764 | ioff += (int)c.length; 765 | break; 766 | } 767 | case ((uint)'I' << 24) + ((uint)'E' << 16) + ((uint)'N' << 8) + 'D': 768 | { 769 | var raw_len = 0; 770 | uint bpl = 0; 771 | if (first != 0) 772 | stbi__err("first not IHDR"); 773 | if (scan != STBI__SCAN_load) 774 | return 1; 775 | if (idata == null) 776 | stbi__err("no IDAT"); 777 | bpl = (uint)((img_x * depth + 7) / 8); 778 | raw_len = (int)(bpl * img_y * img_n + img_y); 779 | expanded = ZLib.stbi_zlib_decode_malloc_guesssize_headerflag(idata, ioff, raw_len, out raw_len, 780 | is_iphone != 0 ? 0 : 1); 781 | if (expanded == null) 782 | return 0; 783 | idata = null; 784 | if (req_comp == img_n + 1 && req_comp != 3 && pal_img_n == 0 || has_trans != 0) 785 | img_out_n = img_n + 1; 786 | else 787 | img_out_n = img_n; 788 | if (stbi__create_png_image(new FakePtr(expanded), (uint)raw_len, img_out_n, depth, color, 789 | interlace) == 0) 790 | return 0; 791 | if (has_trans != 0) 792 | { 793 | if (depth == 16) 794 | { 795 | if (stbi__compute_transparency16(tc16, img_out_n) == 0) 796 | return 0; 797 | } 798 | else 799 | { 800 | if (stbi__compute_transparency(tc, img_out_n) == 0) 801 | return 0; 802 | } 803 | } 804 | 805 | if (is_iphone != 0 && stbi__de_iphone_flag != 0 && img_out_n > 2) 806 | stbi__de_iphone(); 807 | if (pal_img_n != 0) 808 | { 809 | img_n = pal_img_n; 810 | img_out_n = pal_img_n; 811 | if (req_comp >= 3) 812 | img_out_n = req_comp; 813 | if (stbi__expand_png_palette(palette, (int)pal_len, img_out_n) == 0) 814 | return 0; 815 | } 816 | else if (has_trans != 0) 817 | { 818 | ++img_n; 819 | } 820 | 821 | expanded = null; 822 | return 1; 823 | } 824 | default: 825 | if (first != 0) 826 | stbi__err("first not IHDR"); 827 | if ((c.type & (1 << 29)) == 0) 828 | { 829 | var invalid_chunk = c.type + " PNG chunk not known"; 830 | stbi__err(invalid_chunk); 831 | } 832 | 833 | stbi__skip((int)c.length); 834 | break; 835 | } 836 | 837 | stbi__get32be(); 838 | } 839 | } 840 | 841 | private ImageResult InternalDecode(ColorComponents? requiredComponents) 842 | { 843 | var req_comp = requiredComponents.ToReqComp(); 844 | if (req_comp < 0 || req_comp > 4) 845 | stbi__err("bad req_comp"); 846 | 847 | try 848 | { 849 | if (stbi__parse_png_file(STBI__SCAN_load, req_comp) == 0) stbi__err("could not parse png"); 850 | 851 | var bits_per_channel = 8; 852 | if (depth < 8) 853 | bits_per_channel = 8; 854 | else 855 | bits_per_channel = depth; 856 | var result = _out_; 857 | _out_ = null; 858 | if (req_comp != 0 && req_comp != img_out_n) 859 | { 860 | if (bits_per_channel == 8) 861 | result = Conversion.stbi__convert_format(result, img_out_n, req_comp, (uint)img_x, 862 | (uint)img_y); 863 | else 864 | result = Conversion.stbi__convert_format16(result, img_out_n, req_comp, (uint)img_x, 865 | (uint)img_y); 866 | img_out_n = req_comp; 867 | } 868 | 869 | return new ImageResult 870 | { 871 | Width = img_x, 872 | Height = img_y, 873 | SourceComponents = (ColorComponents)img_n, 874 | ColorComponents = requiredComponents != null ? requiredComponents.Value : (ColorComponents)img_n, 875 | BitsPerChannel = bits_per_channel, 876 | Data = result 877 | }; 878 | } 879 | finally 880 | { 881 | _out_ = null; 882 | expanded = null; 883 | idata = null; 884 | } 885 | } 886 | 887 | public static bool Test(Stream stream) 888 | { 889 | var r = stbi__check_png_header(stream); 890 | stream.Rewind(); 891 | 892 | return r; 893 | } 894 | 895 | public static ImageInfo? Info(Stream stream) 896 | { 897 | var decoder = new PngDecoder(stream); 898 | var r = decoder.stbi__parse_png_file(STBI__SCAN_header, 0); 899 | stream.Rewind(); 900 | 901 | if (r == 0) return null; 902 | 903 | return new ImageInfo 904 | { 905 | Width = decoder.img_x, 906 | Height = decoder.img_y, 907 | ColorComponents = (ColorComponents)decoder.img_n, 908 | BitsPerChannel = decoder.depth 909 | }; 910 | } 911 | 912 | public static ImageResult Decode(Stream stream, ColorComponents? requiredComponents = null) 913 | { 914 | var decoder = new PngDecoder(stream); 915 | return decoder.InternalDecode(requiredComponents); 916 | } 917 | } 918 | } --------------------------------------------------------------------------------