diff --git a/LibreMetaverse.GUI/LibreMetaverse.GUI.csproj b/LibreMetaverse.GUI/LibreMetaverse.GUI.csproj index cd9cde1c..9139d362 100644 --- a/LibreMetaverse.GUI/LibreMetaverse.GUI.csproj +++ b/LibreMetaverse.GUI/LibreMetaverse.GUI.csproj @@ -15,6 +15,7 @@ Library LibreMetaverse.GUI true + AnyCPU;x64;x86 True @@ -35,6 +36,44 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -53,6 +92,42 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + LibreMetaverse.GUI.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + LibreMetaverse.GUI.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/LibreMetaverse.GUI/MiniMap.cs b/LibreMetaverse.GUI/MiniMap.cs index 3d36924f..06780de6 100644 --- a/LibreMetaverse.GUI/MiniMap.cs +++ b/LibreMetaverse.GUI/MiniMap.cs @@ -30,6 +30,7 @@ using System.Drawing; using System.Windows.Forms; using OpenMetaverse.Imaging; using OpenMetaverse.Assets; +using LibreMetaverse.Imaging; namespace OpenMetaverse.GUI { @@ -43,9 +44,6 @@ namespace OpenMetaverse.GUI private UUID _MapImageID; private GridClient _Client; private Image _MapLayer; - //warning CS0414: The private field `OpenMetaverse.GUI.MiniMap._MousePosition' is assigned but its value is never used - //private Point _MousePosition; - ToolTip _ToolTip; /// /// Gets or sets the GridClient associated with this control @@ -71,13 +69,6 @@ namespace OpenMetaverse.GUI public MiniMap(GridClient client) : this () { InitializeClient(client); - - _ToolTip = new ToolTip(); - _ToolTip.Active = true; - _ToolTip.AutomaticDelay = 1; - - this.MouseHover += new System.EventHandler(MiniMap_MouseHover); - this.MouseMove += new MouseEventHandler(MiniMap_MouseMove); } /// Sets the map layer to the specified bitmap image @@ -187,23 +178,14 @@ namespace OpenMetaverse.GUI } } - void MiniMap_MouseHover(object sender, System.EventArgs e) - { - _ToolTip.SetToolTip(this, "test"); - _ToolTip.Show("test", this); - //TODO: tooltip popup with closest avatar's name, if within range - } - - void MiniMap_MouseMove(object sender, MouseEventArgs e) - { - _ToolTip.Hide(this); - //warning CS0414: The private field `OpenMetaverse.GUI.MiniMap._MousePosition' is assigned but its value is never used - //_MousePosition = e.Location; - } - void Network_OnCurrentSimChanged(object sender, SimChangedEventArgs e) { - if (_Client.Network.Connected) return; + FetchMapLayer(); + } + + private void FetchMapLayer() + { + if (!_Client.Network.Connected) { return; } GridRegion region; if (Client.Grid.GetGridRegion(Client.Network.CurrentSim.Name, GridLayerType.Objects, out region)) @@ -211,16 +193,21 @@ namespace OpenMetaverse.GUI SetMapLayer(null); _MapImageID = region.MapImageID; - ManagedImage nullImage; - Client.Assets.RequestImage(_MapImageID, ImageType.Baked, - delegate(TextureRequestState state, AssetTexture asset) + Client.Assets.RequestImage(_MapImageID, ImageType.Baked, + delegate (TextureRequestState state, AssetTexture asset) + { + if (state == TextureRequestState.Finished) { - if(state == TextureRequestState.Finished) - OpenJPEG.DecodeToImage(asset.AssetData, out nullImage, out _MapLayer); - }); + using (J2KReader reader = new J2KReader(asset.AssetData)) + { + if (!reader.ReadHeader()) { return; } + _MapLayer = reader.DecodeToBitmap(); + } + } + }); } - } + } } } diff --git a/LibreMetaverse.Rendering.Meshmerizer/LibreMetaverse.Rendering.Meshmerizer.csproj b/LibreMetaverse.Rendering.Meshmerizer/LibreMetaverse.Rendering.Meshmerizer.csproj index e2449a96..ca2fe533 100644 --- a/LibreMetaverse.Rendering.Meshmerizer/LibreMetaverse.Rendering.Meshmerizer.csproj +++ b/LibreMetaverse.Rendering.Meshmerizer/LibreMetaverse.Rendering.Meshmerizer.csproj @@ -14,6 +14,7 @@ Library LibreMetaverse.Rendering.Meshmerizer true + AnyCPU;x64;x86 True @@ -32,6 +33,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -49,6 +84,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/LibreMetaverse.Rendering.Simple/LibreMetaverse.Rendering.Simple.csproj b/LibreMetaverse.Rendering.Simple/LibreMetaverse.Rendering.Simple.csproj index 3a537735..fb3f7d9a 100644 --- a/LibreMetaverse.Rendering.Simple/LibreMetaverse.Rendering.Simple.csproj +++ b/LibreMetaverse.Rendering.Simple/LibreMetaverse.Rendering.Simple.csproj @@ -14,6 +14,7 @@ Library LibreMetaverse.Rendering.Simple true + AnyCPU;x64;x86 True @@ -32,6 +33,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -49,6 +84,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/LibreMetaverse.StructuredData/LibreMetaverse.StructuredData.csproj b/LibreMetaverse.StructuredData/LibreMetaverse.StructuredData.csproj index 8442863e..d3ccdda6 100644 --- a/LibreMetaverse.StructuredData/LibreMetaverse.StructuredData.csproj +++ b/LibreMetaverse.StructuredData/LibreMetaverse.StructuredData.csproj @@ -14,6 +14,7 @@ Library LibreMetaverse.StructuredData true + AnyCPU;x64;x86 True @@ -34,6 +35,44 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -52,6 +91,42 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + LibreMetaverse.StructuredData.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + LibreMetaverse.StructuredData.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/LibreMetaverse.Tests/LibreMetaverse.Tests.csproj b/LibreMetaverse.Tests/LibreMetaverse.Tests.csproj index 0f532b19..1db47c37 100644 --- a/LibreMetaverse.Tests/LibreMetaverse.Tests.csproj +++ b/LibreMetaverse.Tests/LibreMetaverse.Tests.csproj @@ -15,6 +15,7 @@ LibreMetaverse.Tests true false + AnyCPU;x64;x86 True @@ -35,6 +36,44 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -54,6 +93,44 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + + + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + + + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/LibreMetaverse.Utilities/LibreMetaverse.Utilities.csproj b/LibreMetaverse.Utilities/LibreMetaverse.Utilities.csproj index 50a39267..6286b383 100644 --- a/LibreMetaverse.Utilities/LibreMetaverse.Utilities.csproj +++ b/LibreMetaverse.Utilities/LibreMetaverse.Utilities.csproj @@ -14,6 +14,7 @@ Library LibreMetaverse.Utilities true + AnyCPU;x64;x86 True @@ -34,6 +35,44 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + + + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -52,6 +91,42 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + LibreMetaverse.Utilities.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + LibreMetaverse.Utilities.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/LibreMetaverse.sln b/LibreMetaverse.sln index 28081ab0..20086a2b 100644 --- a/LibreMetaverse.sln +++ b/LibreMetaverse.sln @@ -35,109 +35,307 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VoiceTest", "Programs\Voice EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse.PrimMesher", "PrimMesher\LibreMetaverse.PrimMesher.csproj", "{2E2B643F-F18B-4791-BA4B-6E82D0E794B6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mapgenerator", "Programs\mapgenerator\mapgenerator.csproj", "{2867B4B3-0000-0000-0000-000000000000}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mapgenerator", "Programs\mapgenerator\mapgenerator.csproj", "{2867B4B3-0000-0000-0000-000000000000}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 ReleaseNoGui|Any CPU = ReleaseNoGui|Any CPU + ReleaseNoGui|x64 = ReleaseNoGui|x64 + ReleaseNoGui|x86 = ReleaseNoGui|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {95F42663-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {95F42663-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95F42663-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {95F42663-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {95F42663-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {95F42663-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {95F42663-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {95F42663-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {95F42663-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {95F42663-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {95F42663-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {95F42663-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {95F42663-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU + {95F42663-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {95F42663-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {95F42663-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {95F42663-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {79B51DAA-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79B51DAA-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79B51DAA-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {79B51DAA-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {79B51DAA-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {79B51DAA-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {79B51DAA-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {79B51DAA-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {79B51DAA-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {79B51DAA-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {79B51DAA-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {79B51DAA-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {79B51DAA-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {79B51DAA-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {79B51DAA-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {79B51DAA-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {79B51DAA-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {79B51DAA-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {89049BBC-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89049BBC-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89049BBC-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {89049BBC-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {89049BBC-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {89049BBC-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {89049BBC-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {89049BBC-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {89049BBC-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {89049BBC-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {89049BBC-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {89049BBC-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {89049BBC-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {89049BBC-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {89049BBC-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {89049BBC-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {89049BBC-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {89049BBC-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {27C70F3A-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27C70F3A-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27C70F3A-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {27C70F3A-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {27C70F3A-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {27C70F3A-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {27C70F3A-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {27C70F3A-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {27C70F3A-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {27C70F3A-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {27C70F3A-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {27C70F3A-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {27C70F3A-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {27C70F3A-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {27C70F3A-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {27C70F3A-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {27C70F3A-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {27C70F3A-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {09C292AF-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09C292AF-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09C292AF-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {09C292AF-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {09C292AF-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {09C292AF-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {09C292AF-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {09C292AF-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {09C292AF-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {09C292AF-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {09C292AF-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {09C292AF-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {09C292AF-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU + {09C292AF-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {09C292AF-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {09C292AF-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {09C292AF-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {95479B1D-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {95479B1D-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95479B1D-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {95479B1D-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {95479B1D-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {95479B1D-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {95479B1D-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {95479B1D-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {95479B1D-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {95479B1D-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {95479B1D-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {95479B1D-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {95479B1D-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {95479B1D-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {95479B1D-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {95479B1D-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {95479B1D-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {95479B1D-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {29E206AC-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29E206AC-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29E206AC-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {29E206AC-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {29E206AC-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {29E206AC-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {29E206AC-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {29E206AC-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {29E206AC-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {29E206AC-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {29E206AC-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {29E206AC-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {29E206AC-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {29E206AC-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {29E206AC-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {29E206AC-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {29E206AC-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {29E206AC-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {89D7A3E5-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89D7A3E5-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89D7A3E5-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {89D7A3E5-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {89D7A3E5-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {89D7A3E5-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {89D7A3E5-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {89D7A3E5-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {89D7A3E5-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {89D7A3E5-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {89D7A3E5-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {89D7A3E5-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {89D7A3E5-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {89D7A3E5-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {89D7A3E5-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {89D7A3E5-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {89D7A3E5-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {89D7A3E5-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {0CCC2C3D-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0CCC2C3D-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CCC2C3D-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {0CCC2C3D-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {0CCC2C3D-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {0CCC2C3D-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {0CCC2C3D-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {0CCC2C3D-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {0CCC2C3D-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {0CCC2C3D-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {0CCC2C3D-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {0CCC2C3D-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {0CCC2C3D-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {0CCC2C3D-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {0CCC2C3D-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {0CCC2C3D-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {0CCC2C3D-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {0CCC2C3D-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {1266CE08-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1266CE08-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1266CE08-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {1266CE08-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {1266CE08-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {1266CE08-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {1266CE08-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {1266CE08-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {1266CE08-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {1266CE08-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {1266CE08-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {1266CE08-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {1266CE08-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {1266CE08-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {1266CE08-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {1266CE08-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {1266CE08-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {1266CE08-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {B37B02AD-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B37B02AD-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B37B02AD-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {B37B02AD-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {B37B02AD-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {B37B02AD-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {B37B02AD-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {B37B02AD-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {B37B02AD-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {B37B02AD-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {B37B02AD-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {B37B02AD-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {B37B02AD-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {B37B02AD-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {B37B02AD-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {B37B02AD-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {B37B02AD-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {B37B02AD-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {58443010-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58443010-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58443010-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {58443010-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {58443010-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {58443010-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {58443010-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {58443010-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {58443010-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {58443010-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {58443010-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {58443010-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {58443010-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {58443010-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {58443010-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {58443010-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {58443010-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {58443010-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {9F71FDB3-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9F71FDB3-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F71FDB3-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {9F71FDB3-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {9F71FDB3-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {9F71FDB3-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {9F71FDB3-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F71FDB3-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {9F71FDB3-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {9F71FDB3-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {9F71FDB3-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {9F71FDB3-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {9F71FDB3-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {9F71FDB3-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {9F71FDB3-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {9F71FDB3-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {9F71FDB3-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {9F71FDB3-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {EE4EA934-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE4EA934-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE4EA934-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {EE4EA934-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {EE4EA934-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {EE4EA934-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {EE4EA934-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE4EA934-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {EE4EA934-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {EE4EA934-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {EE4EA934-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {EE4EA934-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {EE4EA934-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {EE4EA934-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {EE4EA934-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {EE4EA934-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {EE4EA934-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {EE4EA934-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|x64.ActiveCfg = Debug|x64 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|x64.Build.0 = Debug|x64 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|x86.ActiveCfg = Debug|x86 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Debug|x86.Build.0 = Debug|x86 {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|Any CPU.Build.0 = Release|Any CPU + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|x64.ActiveCfg = Release|x64 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|x64.Build.0 = Release|x64 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|x86.ActiveCfg = Release|x86 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.Release|x86.Build.0 = Release|x86 {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.ReleaseNoGui|x64.Build.0 = Release|x64 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {2E2B643F-F18B-4791-BA4B-6E82D0E794B6}.ReleaseNoGui|x86.Build.0 = Release|x86 {2867B4B3-0000-0000-0000-000000000000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2867B4B3-0000-0000-0000-000000000000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.Debug|x64.ActiveCfg = Debug|x64 + {2867B4B3-0000-0000-0000-000000000000}.Debug|x64.Build.0 = Debug|x64 + {2867B4B3-0000-0000-0000-000000000000}.Debug|x86.ActiveCfg = Debug|x86 + {2867B4B3-0000-0000-0000-000000000000}.Debug|x86.Build.0 = Debug|x86 {2867B4B3-0000-0000-0000-000000000000}.Release|Any CPU.ActiveCfg = Release|Any CPU {2867B4B3-0000-0000-0000-000000000000}.Release|Any CPU.Build.0 = Release|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.Release|x64.ActiveCfg = Release|x64 + {2867B4B3-0000-0000-0000-000000000000}.Release|x64.Build.0 = Release|x64 + {2867B4B3-0000-0000-0000-000000000000}.Release|x86.ActiveCfg = Release|x86 + {2867B4B3-0000-0000-0000-000000000000}.Release|x86.Build.0 = Release|x86 {2867B4B3-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU {2867B4B3-0000-0000-0000-000000000000}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU + {2867B4B3-0000-0000-0000-000000000000}.ReleaseNoGui|x64.ActiveCfg = Release|x64 + {2867B4B3-0000-0000-0000-000000000000}.ReleaseNoGui|x64.Build.0 = Release|x64 + {2867B4B3-0000-0000-0000-000000000000}.ReleaseNoGui|x86.ActiveCfg = Release|x86 + {2867B4B3-0000-0000-0000-000000000000}.ReleaseNoGui|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/LibreMetaverse/Assets/AssetTypes/AssetTexture.cs b/LibreMetaverse/Assets/AssetTypes/AssetTexture.cs index 60aea7b0..be3e1399 100644 --- a/LibreMetaverse/Assets/AssetTypes/AssetTexture.cs +++ b/LibreMetaverse/Assets/AssetTypes/AssetTexture.cs @@ -1,5 +1,6 @@ /* * Copyright (c) 2006-2016, openmetaverse.co + * Copyright (c) 2021, Sjofn LLC. * All rights reserved. * * - Redistribution and use in source and binary forms, with or without @@ -25,7 +26,8 @@ */ using System; -using OpenMetaverse; +using System.Runtime.InteropServices; +using LibreMetaverse.Imaging; using OpenMetaverse.Imaging; namespace OpenMetaverse.Assets @@ -41,9 +43,6 @@ namespace OpenMetaverse.Assets /// A object containing image data public ManagedImage Image; - /// - public OpenJPEG.J2KLayerInfo[] LayerInfo; - /// public int Components; @@ -81,7 +80,10 @@ namespace OpenMetaverse.Assets /// public override void Encode() { - AssetData = OpenJPEG.Encode(Image); + using (J2KWriter writer = new J2KWriter(Image.ExportBitmap())) + { + AssetData = writer.Encode(); + } } /// @@ -91,36 +93,27 @@ namespace OpenMetaverse.Assets /// True if the decoding was successful, otherwise false public override bool Decode() { - if (AssetData != null && AssetData.Length > 0) + if (AssetData == null || AssetData.Length <= 0) { return false; } + + this.Components = 0; + + using (J2KReader reader = new J2KReader(AssetData)) { - this.Components = 0; - - if (OpenJPEG.DecodeToImage(AssetData, out Image)) - { - if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0) - Components += 3; - if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0) - ++Components; - if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0) - ++Components; - if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0) - ++Components; - - return true; - } + // *hack: decode from ManagedImage directly or better yet, get rid of ManagedImage entirely! + if (!reader.ReadHeader()) { return false; } + Image = new ManagedImage(reader.DecodeToBitmap()); } - return false; - } + if ((Image.Channels & ManagedImage.ImageChannels.Color) != 0) + Components += 3; + if ((Image.Channels & ManagedImage.ImageChannels.Gray) != 0) + ++Components; + if ((Image.Channels & ManagedImage.ImageChannels.Bump) != 0) + ++Components; + if ((Image.Channels & ManagedImage.ImageChannels.Alpha) != 0) + ++Components; - /// - /// Decodes the begin and end byte positions for each quality layer in - /// the image - /// - /// - public bool DecodeLayerBoundaries() - { - return OpenJPEG.DecodeLayerBoundaries(AssetData, out LayerInfo, out Components); + return true; } } } diff --git a/LibreMetaverse/Imaging/BakeLayer.cs b/LibreMetaverse/Imaging/BakeLayer.cs index a5598670..d91fe235 100644 --- a/LibreMetaverse/Imaging/BakeLayer.cs +++ b/LibreMetaverse/Imaging/BakeLayer.cs @@ -1,165 +1,165 @@ -/* - * Copyright (c) 2007-2008, openmetaverse.co - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.co nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Reflection; -using System.Collections.Generic; -using System.IO; -using System.Drawing; -using OpenMetaverse.Assets; - -namespace OpenMetaverse.Imaging -{ - /// - /// A set of textures that are layered on texture of each other and "baked" - /// in to a single texture, for avatar appearances - /// - public class Baker - { - #region Properties - /// Final baked texture - public AssetTexture BakedTexture => bakedTexture; - - /// Component layers - public List Textures => textures; - - /// Width of the final baked image and scratchpad - public int BakeWidth => bakeWidth; - - /// Height of the final baked image and scratchpad - public int BakeHeight => bakeHeight; - - /// Bake type - public BakeType BakeType => bakeType; - - /// Is this one of the 3 skin bakes - private bool IsSkin => bakeType == BakeType.Head || bakeType == BakeType.LowerBody || bakeType == BakeType.UpperBody; - - #endregion - - #region Private fields - /// Final baked texture - private AssetTexture bakedTexture; - /// Component layers - private List textures = new List(); - /// Width of the final baked image and scratchpad - private int bakeWidth; - /// Height of the final baked image and scratchpad - private int bakeHeight; - /// Bake type - private BakeType bakeType; - #endregion - - #region Constructor - /// - /// Default constructor - /// - /// Bake type - public Baker(BakeType bakeType) - { - this.bakeType = bakeType; - - if (bakeType == BakeType.Eyes) - { - bakeWidth = 128; - bakeHeight = 128; - } - else - { - bakeWidth = 1024; - bakeHeight = 1024; - } - } - #endregion - - #region Public methods - /// - /// Adds layer for baking - /// - /// TexturaData struct that contains texture and its params - public void AddTexture(AppearanceManager.TextureData tdata) - { - lock (textures) - { - textures.Add(tdata); - } - } - - public void Bake() - { - bakedTexture = new AssetTexture(new ManagedImage(bakeWidth, bakeHeight, - ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump)); - - // These are for head baking, they get special treatment - AppearanceManager.TextureData skinTexture = new AppearanceManager.TextureData(); - List tattooTextures = new List(); - List alphaWearableTextures = new List(); - - // Base color for eye bake is white, color of layer0 for others - if (bakeType == BakeType.Eyes) - { - InitBakedLayerColor(Color4.White); - } - else if (textures.Count > 0) - { - InitBakedLayerColor(textures[0].Color); - } - - // Sort out the special layers we need for head baking and alpha - foreach (AppearanceManager.TextureData tex in textures) - { - if (tex.Texture == null) - continue; - - switch (tex.TextureIndex) - { - case AvatarTextureIndex.HeadBodypaint: - case AvatarTextureIndex.UpperBodypaint: - case AvatarTextureIndex.LowerBodypaint: - skinTexture = tex; - break; - case AvatarTextureIndex.HeadTattoo: - case AvatarTextureIndex.UpperTattoo: - case AvatarTextureIndex.LowerTattoo: - tattooTextures.Add(tex); - break; - } - - if (tex.TextureIndex >= AvatarTextureIndex.LowerAlpha && - tex.TextureIndex <= AvatarTextureIndex.HairAlpha) - { - if (tex.Texture.Image.Alpha != null) - { - alphaWearableTextures.Add(tex.Texture.Image.Clone()); - } - } - } - - if (bakeType == BakeType.Head) - { +/* + * Copyright (c) 2007-2008, openmetaverse.co + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.co nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Reflection; +using System.Collections.Generic; +using System.IO; +using System.Drawing; +using OpenMetaverse.Assets; + +namespace OpenMetaverse.Imaging +{ + /// + /// A set of textures that are layered on texture of each other and "baked" + /// in to a single texture, for avatar appearances + /// + public class Baker + { + #region Properties + /// Final baked texture + public AssetTexture BakedTexture => bakedTexture; + + /// Component layers + public List Textures => textures; + + /// Width of the final baked image and scratchpad + public int BakeWidth => bakeWidth; + + /// Height of the final baked image and scratchpad + public int BakeHeight => bakeHeight; + + /// Bake type + public BakeType BakeType => bakeType; + + /// Is this one of the 3 skin bakes + private bool IsSkin => bakeType == BakeType.Head || bakeType == BakeType.LowerBody || bakeType == BakeType.UpperBody; + + #endregion + + #region Private fields + /// Final baked texture + private AssetTexture bakedTexture; + /// Component layers + private List textures = new List(); + /// Width of the final baked image and scratchpad + private int bakeWidth; + /// Height of the final baked image and scratchpad + private int bakeHeight; + /// Bake type + private BakeType bakeType; + #endregion + + #region Constructor + /// + /// Default constructor + /// + /// Bake type + public Baker(BakeType bakeType) + { + this.bakeType = bakeType; + + if (bakeType == BakeType.Eyes) + { + bakeWidth = 128; + bakeHeight = 128; + } + else + { + bakeWidth = 1024; + bakeHeight = 1024; + } + } + #endregion + + #region Public methods + /// + /// Adds layer for baking + /// + /// TexturaData struct that contains texture and its params + public void AddTexture(AppearanceManager.TextureData tdata) + { + lock (textures) + { + textures.Add(tdata); + } + } + + public void Bake() + { + bakedTexture = new AssetTexture(new ManagedImage(bakeWidth, bakeHeight, + ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump)); + + // These are for head baking, they get special treatment + AppearanceManager.TextureData skinTexture = new AppearanceManager.TextureData(); + List tattooTextures = new List(); + List alphaWearableTextures = new List(); + + // Base color for eye bake is white, color of layer0 for others + if (bakeType == BakeType.Eyes) + { + InitBakedLayerColor(Color4.White); + } + else if (textures.Count > 0) + { + InitBakedLayerColor(textures[0].Color); + } + + // Sort out the special layers we need for head baking and alpha + foreach (AppearanceManager.TextureData tex in textures) + { + if (tex.Texture == null) + continue; + + switch (tex.TextureIndex) + { + case AvatarTextureIndex.HeadBodypaint: + case AvatarTextureIndex.UpperBodypaint: + case AvatarTextureIndex.LowerBodypaint: + skinTexture = tex; + break; + case AvatarTextureIndex.HeadTattoo: + case AvatarTextureIndex.UpperTattoo: + case AvatarTextureIndex.LowerTattoo: + tattooTextures.Add(tex); + break; + } + + if (tex.TextureIndex >= AvatarTextureIndex.LowerAlpha && + tex.TextureIndex <= AvatarTextureIndex.HairAlpha) + { + if (tex.Texture.Image.Alpha != null) + { + alphaWearableTextures.Add(tex.Texture.Image.Clone()); + } + } + } + + if (bakeType == BakeType.Head) + { if (DrawLayer(LoadResourceLayer("head_color.tga"), false) == true) { AddAlpha(bakedTexture.Image, LoadResourceLayer("head_alpha.tga")); @@ -169,322 +169,322 @@ namespace OpenMetaverse.Imaging else { Logger.Log("[Bake]: Unable to draw layer from texture file", Helpers.LogLevel.Debug); - } - } - - if (skinTexture.Texture == null) - { - if (bakeType == BakeType.UpperBody) - { - DrawLayer(LoadResourceLayer("upperbody_color.tga"), false); - } - - if (bakeType == BakeType.LowerBody) - { - DrawLayer(LoadResourceLayer("lowerbody_color.tga"), false); - } - } - - // Layer each texture on top of one other, applying alpha masks as we go - for (int i = 0; i < textures.Count; i++) - { - // Skip if we have no texture on this layer - if (textures[i].Texture == null) continue; - - // Is this Alpha wearable and does it have an alpha channel? - if (textures[i].TextureIndex >= AvatarTextureIndex.LowerAlpha && - textures[i].TextureIndex <= AvatarTextureIndex.HairAlpha) - continue; - - // Don't draw skin and tattoo on head bake first - // For head bake the skin and texture are drawn last, go figure - if (bakeType == BakeType.Head && - (textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || - textures[i].TextureIndex == AvatarTextureIndex.HeadTattoo)) - continue; - - ManagedImage texture = textures[i].Texture.Image.Clone(); - //File.WriteAllBytes(bakeType + "-texture-layer-" + textures[i].TextureIndex + "-" + i + ".tga", texture.ExportTGA()); - - // Resize texture to the size of baked layer - // FIXME: if texture is smaller than the layer, don't stretch it, tile it - if (texture.Width != bakeWidth || texture.Height != bakeHeight) - { - try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } - catch (Exception) { continue; } - } - - // Special case for hair layer for the head bake - // If we don't have skin texture, we discard hair alpha - // and apply hair(i==2) pattern over the texture - if (skinTexture.Texture == null && bakeType == BakeType.Head && textures[i].TextureIndex == AvatarTextureIndex.Hair) - { - if (texture.Alpha != null) - { - for (int j = 0; j < texture.Alpha.Length; j++) texture.Alpha[j] = (byte)255; - } - MultiplyLayerFromAlpha(texture, LoadResourceLayer("head_hair.tga")); - } - - // Aply tint and alpha masks except for skin that has a texture - // on layer 0 which always overrides other skin settings - if (!(textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || - textures[i].TextureIndex == AvatarTextureIndex.UpperBodypaint || - textures[i].TextureIndex == AvatarTextureIndex.LowerBodypaint)) - { - ApplyTint(texture, textures[i].Color); - - // For hair bake, we skip all alpha masks - // and use one from the texture, for both - // alpha and morph layers - if (bakeType == BakeType.Hair) - { - if (texture.Alpha != null) - { - bakedTexture.Image.Bump = texture.Alpha; - } - else - { - for (int j = 0; j < bakedTexture.Image.Bump.Length; j++) bakedTexture.Image.Bump[j] = byte.MaxValue; - } - } - // Apply parametrized alpha masks - else if (textures[i].AlphaMasks != null && textures[i].AlphaMasks.Count > 0) - { - // Combined mask for the layer, fully transparent to begin with - ManagedImage combinedMask = new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Alpha); - - int addedMasks = 0; - - // First add mask in normal blend mode - foreach (KeyValuePair kvp in textures[i].AlphaMasks) - { - if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue; - - if (kvp.Key.MultiplyBlend == false && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) - { - ApplyAlpha(combinedMask, kvp.Key, kvp.Value); - //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); - addedMasks++; - } - } - - // If there were no mask in normal blend mode make aplha fully opaque - if (addedMasks == 0) for (int l = 0; l < combinedMask.Alpha.Length; l++) combinedMask.Alpha[l] = 255; - - // Add masks in multiply blend mode - foreach (KeyValuePair kvp in textures[i].AlphaMasks) - { - if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue; - - if (kvp.Key.MultiplyBlend && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) - { - ApplyAlpha(combinedMask, kvp.Key, kvp.Value); - //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); - addedMasks++; - } - } - - if (addedMasks > 0) - { - // Apply combined alpha mask to the cloned texture - AddAlpha(texture, combinedMask); - } - - // Is this layer used for morph mask? If it is, use its - // alpha as the morth for the whole bake - if (Textures[i].TextureIndex == AppearanceManager.MorphLayerForBakeType(bakeType)) - { - bakedTexture.Image.Bump = texture.Alpha; - } - - //File.WriteAllBytes(bakeType + "-masked-texture-" + i + ".tga", texture.ExportTGA()); - } - } - - bool useAlpha = i == 0 && (BakeType == BakeType.Skirt || BakeType == BakeType.Hair); - DrawLayer(texture, useAlpha); - //File.WriteAllBytes(bakeType + "-layer-" + i + ".tga", texture.ExportTGA()); - } - - // For head and tattoo, we add skin last - if (bakeType == BakeType.Head) - { - if (skinTexture.Texture != null) - { - ManagedImage texture = skinTexture.Texture.Image.Clone(); - if (texture.Width != bakeWidth || texture.Height != bakeHeight) - { - try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } - catch (Exception) { } - } - DrawLayer(texture, false); - } - - foreach (AppearanceManager.TextureData tex in tattooTextures) - { - // Add head tattoo here (if available, order-dependant) - if (tex.Texture != null) - { - ManagedImage texture = tex.Texture.Image.Clone(); - if (texture.Width != bakeWidth || texture.Height != bakeHeight) - { - try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } - catch (Exception) { } - } - DrawLayer(texture, false); - } - } - } - - // Apply any alpha wearable textures to make parts of the avatar disappear - Logger.Log("[XBakes]: Number of alpha wearable textures: " + alphaWearableTextures.Count.ToString(), Helpers.LogLevel.Debug); - foreach (ManagedImage img in alphaWearableTextures) - AddAlpha(bakedTexture.Image, img); - - // We are done, encode asset for finalized bake - bakedTexture.Encode(); - //File.WriteAllBytes(bakeType + ".tga", bakedTexture.Image.ExportTGA()); - } - - private static object ResourceSync = new object(); - - public static ManagedImage LoadResourceLayer(string fileName) - { - try - { - Bitmap bitmap = null; - lock (ResourceSync) - { - using (Stream stream = Helpers.GetResourceStream(fileName, Settings.RESOURCE_DIR)) + } + } + + if (skinTexture.Texture == null) + { + if (bakeType == BakeType.UpperBody) + { + DrawLayer(LoadResourceLayer("upperbody_color.tga"), false); + } + + if (bakeType == BakeType.LowerBody) + { + DrawLayer(LoadResourceLayer("lowerbody_color.tga"), false); + } + } + + // Layer each texture on top of one other, applying alpha masks as we go + for (int i = 0; i < textures.Count; i++) + { + // Skip if we have no texture on this layer + if (textures[i].Texture == null) continue; + + // Is this Alpha wearable and does it have an alpha channel? + if (textures[i].TextureIndex >= AvatarTextureIndex.LowerAlpha && + textures[i].TextureIndex <= AvatarTextureIndex.HairAlpha) + continue; + + // Don't draw skin and tattoo on head bake first + // For head bake the skin and texture are drawn last, go figure + if (bakeType == BakeType.Head && + (textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || + textures[i].TextureIndex == AvatarTextureIndex.HeadTattoo)) + continue; + + ManagedImage texture = textures[i].Texture.Image.Clone(); + //File.WriteAllBytes(bakeType + "-texture-layer-" + textures[i].TextureIndex + "-" + i + ".tga", texture.ExportTGA()); + + // Resize texture to the size of baked layer + // FIXME: if texture is smaller than the layer, don't stretch it, tile it + if (texture.Width != bakeWidth || texture.Height != bakeHeight) + { + try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } + catch (Exception) { continue; } + } + + // Special case for hair layer for the head bake + // If we don't have skin texture, we discard hair alpha + // and apply hair(i==2) pattern over the texture + if (skinTexture.Texture == null && bakeType == BakeType.Head && textures[i].TextureIndex == AvatarTextureIndex.Hair) + { + if (texture.Alpha != null) { - if (stream != null) + for (int j = 0; j < texture.Alpha.Length; j++) texture.Alpha[j] = (byte)255; + } + MultiplyLayerFromAlpha(texture, LoadResourceLayer("head_hair.tga")); + } + + // Aply tint and alpha masks except for skin that has a texture + // on layer 0 which always overrides other skin settings + if (!(textures[i].TextureIndex == AvatarTextureIndex.HeadBodypaint || + textures[i].TextureIndex == AvatarTextureIndex.UpperBodypaint || + textures[i].TextureIndex == AvatarTextureIndex.LowerBodypaint)) + { + ApplyTint(texture, textures[i].Color); + + // For hair bake, we skip all alpha masks + // and use one from the texture, for both + // alpha and morph layers + if (bakeType == BakeType.Hair) + { + if (texture.Alpha != null) + { + bakedTexture.Image.Bump = texture.Alpha; + } + else + { + for (int j = 0; j < bakedTexture.Image.Bump.Length; j++) bakedTexture.Image.Bump[j] = byte.MaxValue; + } + } + // Apply parametrized alpha masks + else if (textures[i].AlphaMasks != null && textures[i].AlphaMasks.Count > 0) + { + // Combined mask for the layer, fully transparent to begin with + ManagedImage combinedMask = new ManagedImage(bakeWidth, bakeHeight, ManagedImage.ImageChannels.Alpha); + + int addedMasks = 0; + + // First add mask in normal blend mode + foreach (KeyValuePair kvp in textures[i].AlphaMasks) + { + if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue; + + if (kvp.Key.MultiplyBlend == false && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) + { + ApplyAlpha(combinedMask, kvp.Key, kvp.Value); + //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); + addedMasks++; + } + } + + // If there were no mask in normal blend mode make aplha fully opaque + if (addedMasks == 0) for (int l = 0; l < combinedMask.Alpha.Length; l++) combinedMask.Alpha[l] = 255; + + // Add masks in multiply blend mode + foreach (KeyValuePair kvp in textures[i].AlphaMasks) + { + if (!MaskBelongsToBake(kvp.Key.TGAFile)) continue; + + if (kvp.Key.MultiplyBlend && (kvp.Value > 0f || !kvp.Key.SkipIfZero)) + { + ApplyAlpha(combinedMask, kvp.Key, kvp.Value); + //File.WriteAllBytes(bakeType + "-layer-" + i + "-mask-" + addedMasks + ".tga", combinedMask.ExportTGA()); + addedMasks++; + } + } + + if (addedMasks > 0) + { + // Apply combined alpha mask to the cloned texture + AddAlpha(texture, combinedMask); + } + + // Is this layer used for morph mask? If it is, use its + // alpha as the morth for the whole bake + if (Textures[i].TextureIndex == AppearanceManager.MorphLayerForBakeType(bakeType)) + { + bakedTexture.Image.Bump = texture.Alpha; + } + + //File.WriteAllBytes(bakeType + "-masked-texture-" + i + ".tga", texture.ExportTGA()); + } + } + + bool useAlpha = i == 0 && (BakeType == BakeType.Skirt || BakeType == BakeType.Hair); + DrawLayer(texture, useAlpha); + //File.WriteAllBytes(bakeType + "-layer-" + i + ".tga", texture.ExportTGA()); + } + + // For head and tattoo, we add skin last + if (bakeType == BakeType.Head) + { + if (skinTexture.Texture != null) + { + ManagedImage texture = skinTexture.Texture.Image.Clone(); + if (texture.Width != bakeWidth || texture.Height != bakeHeight) + { + try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } + catch (Exception) { } + } + DrawLayer(texture, false); + } + + foreach (AppearanceManager.TextureData tex in tattooTextures) + { + // Add head tattoo here (if available, order-dependant) + if (tex.Texture != null) + { + ManagedImage texture = tex.Texture.Image.Clone(); + if (texture.Width != bakeWidth || texture.Height != bakeHeight) + { + try { texture.ResizeNearestNeighbor(bakeWidth, bakeHeight); } + catch (Exception) { } + } + DrawLayer(texture, false); + } + } + } + + // Apply any alpha wearable textures to make parts of the avatar disappear + Logger.Log("[XBakes]: Number of alpha wearable textures: " + alphaWearableTextures.Count.ToString(), Helpers.LogLevel.Debug); + foreach (ManagedImage img in alphaWearableTextures) + AddAlpha(bakedTexture.Image, img); + + // We are done, encode asset for finalized bake + bakedTexture.Encode(); + //File.WriteAllBytes(bakeType + ".tga", bakedTexture.Image.ExportTGA()); + } + + private static object ResourceSync = new object(); + + public static ManagedImage LoadResourceLayer(string fileName) + { + try + { + Bitmap bitmap = null; + lock (ResourceSync) + { + using (Stream stream = Helpers.GetResourceStream(fileName, Settings.RESOURCE_DIR)) + { + if (stream != null) { bitmap = LoadTGAClass.LoadTGA(stream); - } - } - } - if (bitmap == null) - { - Logger.Log(String.Format("Failed loading resource file: {0}", fileName), Helpers.LogLevel.Error); - return null; - } - else - { - return new ManagedImage(bitmap); - } - } - catch (Exception e) - { - Logger.Log(String.Format("Failed loading resource file: {0} ({1})", fileName, e.Message), - Helpers.LogLevel.Error, e); - return null; - } - } - - /// - /// Converts avatar texture index (face) to Bake type - /// - /// Face number (AvatarTextureIndex) - /// BakeType, layer to which this texture belongs to - public static BakeType BakeTypeFor(AvatarTextureIndex index) - { - switch (index) - { - case AvatarTextureIndex.HeadBodypaint: - return BakeType.Head; - - case AvatarTextureIndex.UpperBodypaint: - case AvatarTextureIndex.UpperGloves: - case AvatarTextureIndex.UpperUndershirt: - case AvatarTextureIndex.UpperShirt: - case AvatarTextureIndex.UpperJacket: - return BakeType.UpperBody; - - case AvatarTextureIndex.LowerBodypaint: - case AvatarTextureIndex.LowerUnderpants: - case AvatarTextureIndex.LowerSocks: - case AvatarTextureIndex.LowerShoes: - case AvatarTextureIndex.LowerPants: - case AvatarTextureIndex.LowerJacket: - return BakeType.LowerBody; - - case AvatarTextureIndex.EyesIris: - return BakeType.Eyes; - - case AvatarTextureIndex.Skirt: - return BakeType.Skirt; - - case AvatarTextureIndex.Hair: - return BakeType.Hair; - - default: - return BakeType.Unknown; - } - } - #endregion - - #region Private layer compositing methods - - private bool MaskBelongsToBake(string mask) - { + } + } + } + if (bitmap == null) + { + Logger.Log(String.Format("Failed loading resource file: {0}", fileName), Helpers.LogLevel.Error); + return null; + } + else + { + return new ManagedImage(bitmap); + } + } + catch (Exception e) + { + Logger.Log(String.Format("Failed loading resource file: {0} ({1})", fileName, e.Message), + Helpers.LogLevel.Error, e); + return null; + } + } + + /// + /// Converts avatar texture index (face) to Bake type + /// + /// Face number (AvatarTextureIndex) + /// BakeType, layer to which this texture belongs to + public static BakeType BakeTypeFor(AvatarTextureIndex index) + { + switch (index) + { + case AvatarTextureIndex.HeadBodypaint: + return BakeType.Head; + + case AvatarTextureIndex.UpperBodypaint: + case AvatarTextureIndex.UpperGloves: + case AvatarTextureIndex.UpperUndershirt: + case AvatarTextureIndex.UpperShirt: + case AvatarTextureIndex.UpperJacket: + return BakeType.UpperBody; + + case AvatarTextureIndex.LowerBodypaint: + case AvatarTextureIndex.LowerUnderpants: + case AvatarTextureIndex.LowerSocks: + case AvatarTextureIndex.LowerShoes: + case AvatarTextureIndex.LowerPants: + case AvatarTextureIndex.LowerJacket: + return BakeType.LowerBody; + + case AvatarTextureIndex.EyesIris: + return BakeType.Eyes; + + case AvatarTextureIndex.Skirt: + return BakeType.Skirt; + + case AvatarTextureIndex.Hair: + return BakeType.Hair; + + default: + return BakeType.Unknown; + } + } + #endregion + + #region Private layer compositing methods + + private bool MaskBelongsToBake(string mask) + { return (bakeType != BakeType.LowerBody || !mask.Contains("upper")) && (bakeType != BakeType.LowerBody || !mask.Contains("shirt")) - && (bakeType != BakeType.UpperBody || !mask.Contains("lower")); - } - - private bool DrawLayer(ManagedImage source, bool addSourceAlpha) - { - if (source == null) return false; - - bool sourceHasColor; - bool sourceHasAlpha; - bool sourceHasBump; - int i = 0; - - sourceHasColor = ((source.Channels & ManagedImage.ImageChannels.Color) != 0 && - source.Red != null && source.Green != null && source.Blue != null); - sourceHasAlpha = ((source.Channels & ManagedImage.ImageChannels.Alpha) != 0 && source.Alpha != null); - sourceHasBump = ((source.Channels & ManagedImage.ImageChannels.Bump) != 0 && source.Bump != null); - - addSourceAlpha = (addSourceAlpha && sourceHasAlpha); - - byte alpha = Byte.MaxValue; - byte alphaInv = (byte)(Byte.MaxValue - alpha); - - byte[] bakedRed = bakedTexture.Image.Red; - byte[] bakedGreen = bakedTexture.Image.Green; - byte[] bakedBlue = bakedTexture.Image.Blue; - byte[] bakedAlpha = bakedTexture.Image.Alpha; - byte[] bakedBump = bakedTexture.Image.Bump; - - byte[] sourceRed = source.Red; - byte[] sourceGreen = source.Green; - byte[] sourceBlue = source.Blue; - byte[] sourceAlpha = sourceHasAlpha ? source.Alpha : null; - byte[] sourceBump = sourceHasBump ? source.Bump : null; - - bool loadedAlpha = false; - for (int y = 0; y < bakeHeight; y++) - { - for (int x = 0; x < bakeWidth; x++) - { - loadedAlpha = false; - alpha = 0; + && (bakeType != BakeType.UpperBody || !mask.Contains("lower")); + } + + private bool DrawLayer(ManagedImage source, bool addSourceAlpha) + { + if (source == null) return false; + + bool sourceHasColor; + bool sourceHasAlpha; + bool sourceHasBump; + int i = 0; + + sourceHasColor = ((source.Channels & ManagedImage.ImageChannels.Color) != 0 && + source.Red != null && source.Green != null && source.Blue != null); + sourceHasAlpha = ((source.Channels & ManagedImage.ImageChannels.Alpha) != 0 && source.Alpha != null); + sourceHasBump = ((source.Channels & ManagedImage.ImageChannels.Bump) != 0 && source.Bump != null); + + addSourceAlpha = (addSourceAlpha && sourceHasAlpha); + + byte alpha = Byte.MaxValue; + byte alphaInv = (byte)(Byte.MaxValue - alpha); + + byte[] bakedRed = bakedTexture.Image.Red; + byte[] bakedGreen = bakedTexture.Image.Green; + byte[] bakedBlue = bakedTexture.Image.Blue; + byte[] bakedAlpha = bakedTexture.Image.Alpha; + byte[] bakedBump = bakedTexture.Image.Bump; + + byte[] sourceRed = source.Red; + byte[] sourceGreen = source.Green; + byte[] sourceBlue = source.Blue; + byte[] sourceAlpha = sourceHasAlpha ? source.Alpha : null; + byte[] sourceBump = sourceHasBump ? source.Bump : null; + + bool loadedAlpha = false; + for (int y = 0; y < bakeHeight; y++) + { + for (int x = 0; x < bakeWidth; x++) + { + loadedAlpha = false; + alpha = 0; alphaInv = 0; - if (sourceHasAlpha) - { + if (sourceHasAlpha) + { if (sourceAlpha.Length > i) { loadedAlpha = true; alpha = sourceAlpha[i]; alphaInv = (byte)(Byte.MaxValue - alpha); - } - } - - if (sourceHasColor) - { + } + } + + if (sourceHasColor) + { if ((bakedRed.Length > i) && (bakedGreen.Length > i) && (bakedBlue.Length > i)) { if ((sourceRed.Length > i) && (sourceGreen.Length > i) && (sourceBlue.Length > i)) @@ -502,209 +502,209 @@ namespace OpenMetaverse.Imaging bakedBlue[i] = sourceBlue[i]; } } - } - } - - if (addSourceAlpha) - { + } + } + + if (addSourceAlpha) + { if ((sourceAlpha.Length > i) && (bakedAlpha.Length > i)) { if (sourceAlpha[i] < bakedAlpha[i]) { bakedAlpha[i] = sourceAlpha[i]; } - } - } - + } + } + if (sourceHasBump) { if (sourceBump.Length > i) { bakedBump[i] = sourceBump[i]; } - } - - ++i; - } - } - - return true; - } - - /// - /// Make sure images exist, resize source if needed to match the destination - /// - /// Destination image - /// Source image - /// Sanitization was succefull - private bool SanitizeLayers(ManagedImage dest, ManagedImage src) - { - if (dest == null || src == null) return false; - - if ((dest.Channels & ManagedImage.ImageChannels.Alpha) == 0) - { - dest.ConvertChannels(dest.Channels | ManagedImage.ImageChannels.Alpha); - } - - if (dest.Width != src.Width || dest.Height != src.Height) - { - try { src.ResizeNearestNeighbor(dest.Width, dest.Height); } - catch (Exception) { return false; } - } - - return true; - } - - - private void ApplyAlpha(ManagedImage dest, VisualAlphaParam param, float val) - { - ManagedImage src = LoadResourceLayer(param.TGAFile); - - if (dest == null || src?.Alpha == null) return; - - if ((dest.Channels & ManagedImage.ImageChannels.Alpha) == 0) - { - dest.ConvertChannels(ManagedImage.ImageChannels.Alpha | dest.Channels); - } - - if (dest.Width != src.Width || dest.Height != src.Height) - { - try { src.ResizeNearestNeighbor(dest.Width, dest.Height); } - catch (Exception) { return; } - } - - for (int i = 0; i < dest.Alpha.Length; i++) - { - byte alpha = src.Alpha[i] <= ((1 - val) * 255) ? (byte)0 : (byte)255; - - if (param.MultiplyBlend) - { - dest.Alpha[i] = (byte)((dest.Alpha[i] * alpha) >> 8); - } - else - { - if (alpha > dest.Alpha[i]) - { - dest.Alpha[i] = alpha; - } - } - } - } - - private void AddAlpha(ManagedImage dest, ManagedImage src) - { - if (!SanitizeLayers(dest, src)) return; - - for (int i = 0; i < dest.Alpha.Length; i++) - { - if (src.Alpha[i] < dest.Alpha[i]) - { - dest.Alpha[i] = src.Alpha[i]; - } - } - } - - private void MultiplyLayerFromAlpha(ManagedImage dest, ManagedImage src) - { - if (!SanitizeLayers(dest, src)) return; - - for (int i = 0; i < dest.Red.Length; i++) - { - dest.Red[i] = (byte)((dest.Red[i] * src.Alpha[i]) >> 8); - dest.Green[i] = (byte)((dest.Green[i] * src.Alpha[i]) >> 8); - dest.Blue[i] = (byte)((dest.Blue[i] * src.Alpha[i]) >> 8); - } - } - - private void ApplyTint(ManagedImage dest, Color4 src) - { - if (dest == null) return; - - for (int i = 0; i < dest.Red.Length; i++) - { - dest.Red[i] = (byte)((dest.Red[i] * Utils.FloatToByte(src.R, 0f, 1f)) >> 8); - dest.Green[i] = (byte)((dest.Green[i] * Utils.FloatToByte(src.G, 0f, 1f)) >> 8); - dest.Blue[i] = (byte)((dest.Blue[i] * Utils.FloatToByte(src.B, 0f, 1f)) >> 8); - } - } - - /// - /// Fills a baked layer as a solid *appearing* color. The colors are - /// subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from - /// compressing it too far since it seems to cause upload failures if - /// the image is a pure solid color - /// - /// Color of the base of this layer - private void InitBakedLayerColor(Color4 color) - { - InitBakedLayerColor(color.R, color.G, color.B); - } - - /// - /// Fills a baked layer as a solid *appearing* color. The colors are - /// subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from - /// compressing it too far since it seems to cause upload failures if - /// the image is a pure solid color - /// - /// Red value - /// Green value - /// Blue value - private void InitBakedLayerColor(float r, float g, float b) - { - byte rByte = Utils.FloatToByte(r, 0f, 1f); - byte gByte = Utils.FloatToByte(g, 0f, 1f); - byte bByte = Utils.FloatToByte(b, 0f, 1f); - - var rAlt = rByte; - var gAlt = gByte; - var bAlt = bByte; - - if (rByte < byte.MaxValue) - rAlt++; - else rAlt--; - - if (gByte < byte.MaxValue) - gAlt++; - else gAlt--; - - if (bByte < byte.MaxValue) - bAlt++; - else bAlt--; - - int i = 0; - - byte[] red = bakedTexture.Image.Red; - byte[] green = bakedTexture.Image.Green; - byte[] blue = bakedTexture.Image.Blue; - byte[] alpha = bakedTexture.Image.Alpha; - byte[] bump = bakedTexture.Image.Bump; - - for (int y = 0; y < bakeHeight; y++) - { - for (int x = 0; x < bakeWidth; x++) - { - if (((x ^ y) & 0x10) == 0) - { - red[i] = rAlt; - green[i] = gByte; - blue[i] = bByte; - alpha[i] = byte.MaxValue; - bump[i] = 0; - } - else - { - red[i] = rByte; - green[i] = gAlt; - blue[i] = bAlt; - alpha[i] = byte.MaxValue; - bump[i] = 0; - } - - ++i; - } - } - - } - #endregion - } -} + } + + ++i; + } + } + + return true; + } + + /// + /// Make sure images exist, resize source if needed to match the destination + /// + /// Destination image + /// Source image + /// Sanitization was succefull + private bool SanitizeLayers(ManagedImage dest, ManagedImage src) + { + if (dest == null || src == null) return false; + + if ((dest.Channels & ManagedImage.ImageChannels.Alpha) == 0) + { + dest.ConvertChannels(dest.Channels | ManagedImage.ImageChannels.Alpha); + } + + if (dest.Width != src.Width || dest.Height != src.Height) + { + try { src.ResizeNearestNeighbor(dest.Width, dest.Height); } + catch (Exception) { return false; } + } + + return true; + } + + + private void ApplyAlpha(ManagedImage dest, VisualAlphaParam param, float val) + { + ManagedImage src = LoadResourceLayer(param.TGAFile); + + if (dest == null || src?.Alpha == null) return; + + if ((dest.Channels & ManagedImage.ImageChannels.Alpha) == 0) + { + dest.ConvertChannels(ManagedImage.ImageChannels.Alpha | dest.Channels); + } + + if (dest.Width != src.Width || dest.Height != src.Height) + { + try { src.ResizeNearestNeighbor(dest.Width, dest.Height); } + catch (Exception) { return; } + } + + for (int i = 0; i < dest.Alpha.Length; i++) + { + byte alpha = src.Alpha[i] <= ((1 - val) * 255) ? (byte)0 : (byte)255; + + if (param.MultiplyBlend) + { + dest.Alpha[i] = (byte)((dest.Alpha[i] * alpha) >> 8); + } + else + { + if (alpha > dest.Alpha[i]) + { + dest.Alpha[i] = alpha; + } + } + } + } + + private void AddAlpha(ManagedImage dest, ManagedImage src) + { + if (!SanitizeLayers(dest, src)) return; + + for (int i = 0; i < dest.Alpha.Length; i++) + { + if (src.Alpha[i] < dest.Alpha[i]) + { + dest.Alpha[i] = src.Alpha[i]; + } + } + } + + private void MultiplyLayerFromAlpha(ManagedImage dest, ManagedImage src) + { + if (!SanitizeLayers(dest, src)) return; + + for (int i = 0; i < dest.Red.Length; i++) + { + dest.Red[i] = (byte)((dest.Red[i] * src.Alpha[i]) >> 8); + dest.Green[i] = (byte)((dest.Green[i] * src.Alpha[i]) >> 8); + dest.Blue[i] = (byte)((dest.Blue[i] * src.Alpha[i]) >> 8); + } + } + + private void ApplyTint(ManagedImage dest, Color4 src) + { + if (dest == null) return; + + for (int i = 0; i < dest.Red.Length; i++) + { + dest.Red[i] = (byte)((dest.Red[i] * Utils.FloatToByte(src.R, 0f, 1f)) >> 8); + dest.Green[i] = (byte)((dest.Green[i] * Utils.FloatToByte(src.G, 0f, 1f)) >> 8); + dest.Blue[i] = (byte)((dest.Blue[i] * Utils.FloatToByte(src.B, 0f, 1f)) >> 8); + } + } + + /// + /// Fills a baked layer as a solid *appearing* color. The colors are + /// subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from + /// compressing it too far since it seems to cause upload failures if + /// the image is a pure solid color + /// + /// Color of the base of this layer + private void InitBakedLayerColor(Color4 color) + { + InitBakedLayerColor(color.R, color.G, color.B); + } + + /// + /// Fills a baked layer as a solid *appearing* color. The colors are + /// subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from + /// compressing it too far since it seems to cause upload failures if + /// the image is a pure solid color + /// + /// Red value + /// Green value + /// Blue value + private void InitBakedLayerColor(float r, float g, float b) + { + byte rByte = Utils.FloatToByte(r, 0f, 1f); + byte gByte = Utils.FloatToByte(g, 0f, 1f); + byte bByte = Utils.FloatToByte(b, 0f, 1f); + + var rAlt = rByte; + var gAlt = gByte; + var bAlt = bByte; + + if (rByte < byte.MaxValue) + rAlt++; + else rAlt--; + + if (gByte < byte.MaxValue) + gAlt++; + else gAlt--; + + if (bByte < byte.MaxValue) + bAlt++; + else bAlt--; + + int i = 0; + + byte[] red = bakedTexture.Image.Red; + byte[] green = bakedTexture.Image.Green; + byte[] blue = bakedTexture.Image.Blue; + byte[] alpha = bakedTexture.Image.Alpha; + byte[] bump = bakedTexture.Image.Bump; + + for (int y = 0; y < bakeHeight; y++) + { + for (int x = 0; x < bakeWidth; x++) + { + if (((x ^ y) & 0x10) == 0) + { + red[i] = rAlt; + green[i] = gByte; + blue[i] = bByte; + alpha[i] = byte.MaxValue; + bump[i] = 0; + } + else + { + red[i] = rByte; + green[i] = gAlt; + blue[i] = bAlt; + alpha[i] = byte.MaxValue; + bump[i] = 0; + } + + ++i; + } + } + + } + #endregion + } +} diff --git a/LibreMetaverse/Imaging/ImageHelper.cs b/LibreMetaverse/Imaging/ImageHelper.cs new file mode 100644 index 00000000..7bc036f5 --- /dev/null +++ b/LibreMetaverse/Imaging/ImageHelper.cs @@ -0,0 +1,251 @@ +/** + * Copyright (c) 2021, Sjofn LLC. + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.co nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using OpenJpegDotNet; + +namespace LibreMetaverse.Imaging +{ + + internal static class ImageHelper + { + + #region Methods + + public static OpenJpegDotNet.Image FromRaw(byte[] raw, int width, int height, int stride, int channels, bool interleaved) + { + if (raw == null) + throw new ArgumentNullException(nameof(raw)); + + var byteAllocated = 1; + var colorSpace = ColorSpace.Srgb; + var precision = 24u / (uint)channels; + var gap = stride - width * channels; + + using (var compressionParameters = new CompressionParameters()) + { + OpenJpeg.SetDefaultEncoderParameters(compressionParameters); + + var subsamplingDx = compressionParameters.SubsamplingDx; + var subsamplingDy = compressionParameters.SubsamplingDy; + + var componentParametersArray = new ImageComponentParameters[channels]; + for (var i = 0; i < channels; i++) + { + componentParametersArray[i] = new ImageComponentParameters + { + Precision = precision, + Bpp = precision, + Signed = false, + Dx = (uint)subsamplingDx, + Dy = (uint)subsamplingDy, + Width = (uint)width, + Height = (uint)height + }; + } + + var image = OpenJpeg.ImageCreate((uint)channels, componentParametersArray, colorSpace); + if (image == null) + return null; + + image.X0 = (uint)compressionParameters.ImageOffsetX0; + image.Y0 = (uint)compressionParameters.ImageOffsetY0; + image.X1 = image.X0 == 0 ? (uint)(width - 1) * (uint)subsamplingDx + 1 : image.X0 + (uint)(width - 1) * (uint)subsamplingDx + 1; + image.Y1 = image.Y0 == 0 ? (uint)(height - 1) * (uint)subsamplingDy + 1 : image.Y0 + (uint)(height - 1) * (uint)subsamplingDy + 1; + + unsafe + { + fixed (byte* pRaw = &raw[0]) + { + // Bitmap data is interleave. + // Convert it to planer + if (byteAllocated == 1) + { + if (interleaved) + { + for (var i = 0; i < channels; i++) + { + var target = image.Components[i].Data; + var pTarget = (int*)target; + var source = pRaw + i; + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) + { + *pTarget = *source; + pTarget++; + source += channels; + } + + source += gap; + } + } + } + else + { + for (var i = 0; i < channels; i++) + { + var target = image.Components[i].Data; + var pTarget = (int*)target; + var source = pRaw + i * (stride * height); + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) + { + *pTarget = *source; + pTarget++; + source++; + } + + source += gap; + } + } + } + } + } + } + + return image; + } + } + + public static OpenJpegDotNet.Image FromBitmap(Bitmap bitmap) + { + if (bitmap == null) + throw new ArgumentNullException(nameof(bitmap)); + + var width = bitmap.Width; + var height = bitmap.Height; + var format = bitmap.PixelFormat; + int channels; + var byteAllocated = 0; + ColorSpace colorSpace; + switch (format) + { + case PixelFormat.Format24bppRgb: + channels = 3; + colorSpace = ColorSpace.Srgb; + byteAllocated = 1; + break; + case PixelFormat.Format32bppArgb: + channels = 4; + colorSpace = ColorSpace.Srgb; + byteAllocated = 1; + break; + case PixelFormat.Format8bppIndexed: + channels = 1; + colorSpace = ColorSpace.Srgb; + byteAllocated = 1; + break; + default: + throw new NotSupportedException(); + } + var precision = 24u / (uint)channels; + + BitmapData bitmapData = null; + + try + { + bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, format); + var stride = bitmapData.Stride; + var gap = stride - width * channels; + var scan0 = bitmapData.Scan0; + + using (var compressionParameters = new CompressionParameters()) + { + OpenJpeg.SetDefaultEncoderParameters(compressionParameters); + + var subsamplingDx = compressionParameters.SubsamplingDx; + var subsamplingDy = compressionParameters.SubsamplingDy; + + var componentParametersArray = new ImageComponentParameters[channels]; + for (var i = 0; i < channels; i++) + { + componentParametersArray[i] = new ImageComponentParameters + { + Precision = precision, + Bpp = precision, + Signed = false, + Dx = (uint)subsamplingDx, + Dy = (uint)subsamplingDy, + Width = (uint)width, + Height = (uint)height + }; + } + + var image = OpenJpeg.ImageCreate((uint)channels, componentParametersArray, colorSpace); + if (image == null) + return null; + + image.X0 = (uint)compressionParameters.ImageOffsetX0; + image.Y0 = (uint)compressionParameters.ImageOffsetY0; + image.X1 = image.X0 == 0 ? (uint)(width - 1) * (uint)subsamplingDx + 1 : image.X0 + (uint)(width - 1) * (uint)subsamplingDx + 1; + image.Y1 = image.Y0 == 0 ? (uint)(height - 1) * (uint)subsamplingDy + 1 : image.Y0 + (uint)(height - 1) * (uint)subsamplingDy + 1; + + unsafe + { + // Bitmap data is interleave. + // Convert it to planer + if (byteAllocated == 1) + { + for (var i = 0; i < channels; i++) + { + var target = image.Components[i].Data; + var pTarget = (int*)target; + var source = (byte*)scan0; + source += i; + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) + { + *pTarget = *source; + pTarget++; + source += channels; + } + + source += gap; + } + } + } + } + + return image; + } + } + finally + { + if (bitmapData != null) + bitmap.UnlockBits(bitmapData); + } + } + + #endregion + + } + +} \ No newline at end of file diff --git a/LibreMetaverse/Imaging/J2KBuffer.cs b/LibreMetaverse/Imaging/J2KBuffer.cs new file mode 100644 index 00000000..3b8e18af --- /dev/null +++ b/LibreMetaverse/Imaging/J2KBuffer.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibreMetaverse.Imaging +{ + [StructLayout(LayoutKind.Sequential)] + internal struct J2KBuffer + { + + public IntPtr Data; + + public int Length; + + public int Position; + + } +} diff --git a/LibreMetaverse/Imaging/J2KReader.cs b/LibreMetaverse/Imaging/J2KReader.cs new file mode 100644 index 00000000..ac3f27e4 --- /dev/null +++ b/LibreMetaverse/Imaging/J2KReader.cs @@ -0,0 +1,259 @@ +/** + * Copyright (c) 2021, Sjofn LLC. + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.co nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using OpenJpegDotNet; + +namespace LibreMetaverse.Imaging +{ + public sealed class J2KReader : IDisposable + { + + #region Fields + + private readonly J2KBuffer _Buffer; + + private readonly IntPtr _UserData; + + private readonly DelegateHandler _ReadCallback; + + private readonly DelegateHandler _SeekCallback; + + private readonly DelegateHandler _SkipCallback; + + private Codec _Codec; + + private DecompressionParameters _DecompressionParameters; + + private OpenJpegDotNet.Image _Image; + + private readonly Stream _Stream; + + #endregion + + #region Constructors + + public J2KReader(byte[] data) + { + this._Buffer = new J2KBuffer + { + Data = Marshal.AllocHGlobal(data.Length), + Length = data.Length, + Position = 0 + }; + + Marshal.Copy(data, 0, this._Buffer.Data, this._Buffer.Length); + + var size = Marshal.SizeOf(this._Buffer); + this._UserData = Marshal.AllocHGlobal(size); + Marshal.StructureToPtr(this._Buffer, this._UserData, false); + + this._ReadCallback = new DelegateHandler(Read); + this._SeekCallback = new DelegateHandler(Seek); + this._SkipCallback = new DelegateHandler(Skip); + + this._Stream = OpenJpeg.StreamDefaultCreate(true); + OpenJpeg.StreamSetUserData(this._Stream, this._UserData); + OpenJpeg.StreamSetUserDataLength(this._Stream, this._Buffer.Length); + OpenJpeg.StreamSetReadFunction(this._Stream, this._ReadCallback); + OpenJpeg.StreamSetSeekFunction(this._Stream, this._SeekCallback); + OpenJpeg.StreamSetSkipFunction(this._Stream, this._SkipCallback); + } + + #endregion + + #region Properties + + public int Height + { + get; + private set; + } + + /// + /// Gets a value indicating whether this instance has been disposed. + /// + /// true if this instance has been disposed; otherwise, false. + public bool IsDisposed + { + get; + private set; + } + + public int Width + { + get; + private set; + } + + #endregion + + #region Methods + + public bool ReadHeader() + { + this._Codec?.Dispose(); + this._DecompressionParameters?.Dispose(); + this._Image?.Dispose(); + + this._Codec = null; + this._DecompressionParameters = null; + this._Image = null; + + this._Codec = OpenJpeg.CreateDecompress(CodecFormat.J2k); + this._DecompressionParameters = new DecompressionParameters(); + OpenJpeg.SetDefaultDecoderParameters(this._DecompressionParameters); + + if (!OpenJpeg.SetupDecoder(this._Codec, this._DecompressionParameters)) + return false; + + if (!OpenJpeg.ReadHeader(this._Stream, this._Codec, out var image)) + return false; + + this.Width = (int)(image.X1 - image.X0); + this.Height = (int)(image.Y1 - image.Y0); + this._Image = image; + + return true; + } + + public OpenJpegDotNet.Image Decode() + { + if (this._Image == null || this._Image.IsDisposed) + throw new InvalidOperationException(); + + if (!OpenJpeg.Decode(this._Codec, this._Stream, this._Image)) + throw new InvalidOperationException(); + + return this._Image; + } + + public Bitmap DecodeToBitmap() + { + if (this._Image == null || this._Image.IsDisposed) + throw new InvalidOperationException(); + + if (!OpenJpeg.Decode(this._Codec, this._Stream, this._Image)) + throw new InvalidOperationException(); + + return this._Image.ToBitmap(); + } + + #region Event Handlers + + private static ulong Read(IntPtr buffer, ulong bytes, IntPtr userData) + { + unsafe + { + var buf = (J2KBuffer*)userData; + var bytesToRead = (int)Math.Min((ulong)(buf->Length - buf->Position), bytes); + if (bytesToRead > 0) + { + NativeMethods.cstd_memcpy(buffer, IntPtr.Add(buf->Data, buf->Position), bytesToRead); + buf->Position += bytesToRead; + return (ulong)bytesToRead; + } + else + { + return unchecked((ulong)-1); + } + } + } + + private static int Seek(ulong bytes, IntPtr userData) + { + unsafe + { + var buf = (J2KBuffer*)userData; + var position = Math.Min((ulong)buf->Length, bytes); + buf->Position = (int)position; + return 1; + } + } + + private static long Skip(ulong bytes, IntPtr userData) + { + unsafe + { + var buf = (J2KBuffer*)userData; + var bytesToSkip = (int)Math.Min((ulong)buf->Length, bytes); + if (bytesToSkip > 0) + { + buf->Position += bytesToSkip; + return bytesToSkip; + } + else + { + return unchecked(-1); + } + } + } + + #endregion + + #endregion + + #region IDisposable Members + + /// + /// Releases all resources used by this . + /// + public void Dispose() + { + this.Dispose(true); + //GC.SuppressFinalize(this); + } + + /// + /// Releases all resources used by this . + /// + /// Indicate value whether method was called. + private void Dispose(bool disposing) + { + if (this.IsDisposed) + { + return; + } + + this.IsDisposed = true; + + if (disposing) + { + this._Codec?.Dispose(); + this._DecompressionParameters?.Dispose(); + this._Stream.Dispose(); + + Marshal.FreeHGlobal(this._Buffer.Data); + Marshal.FreeHGlobal(this._UserData); + } + } + + #endregion + + } +} diff --git a/LibreMetaverse/Imaging/J2KWriter.cs b/LibreMetaverse/Imaging/J2KWriter.cs new file mode 100644 index 00000000..8492de78 --- /dev/null +++ b/LibreMetaverse/Imaging/J2KWriter.cs @@ -0,0 +1,254 @@ +/** + * Copyright (c) 2021, Sjofn LLC. + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.co nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using OpenJpegDotNet; + +namespace LibreMetaverse.Imaging +{ + public sealed class J2KWriter : IDisposable + { + + #region Fields + + private readonly J2KBuffer _Buffer; + + private readonly IntPtr _UserData; + + private readonly DelegateHandler _WriteCallback; + + private readonly DelegateHandler _SeekCallback; + + private readonly DelegateHandler _SkipCallback; + + private Codec _Codec; + + private CompressionParameters _CompressionParameters; + + private OpenJpegDotNet.Image _Image; + + private readonly Stream _Stream; + + #endregion + + #region Constructors + + public J2KWriter(Bitmap bitmap) + { + _Image = ImageHelper.FromBitmap(bitmap); + int datalen = (int)(_Image.X1 * _Image.Y1 * _Image.NumberOfComponents + 1024); + + this._Buffer = new J2KBuffer + { + Data = Marshal.AllocHGlobal(datalen), + Length = datalen, + Position = 0 + }; + + var size = Marshal.SizeOf(this._Buffer); + this._UserData = Marshal.AllocHGlobal(size); + Marshal.StructureToPtr(this._Buffer, this._UserData, false); + + this._WriteCallback = new DelegateHandler(Write); + this._SeekCallback = new DelegateHandler(Seek); + this._SkipCallback = new DelegateHandler(Skip); + + this._Stream = OpenJpeg.StreamCreate((ulong)_Buffer.Length, false); + OpenJpeg.StreamSetUserData(this._Stream, this._UserData); + OpenJpeg.StreamSetUserDataLength(this._Stream, this._Buffer.Length); + OpenJpeg.StreamSetWriteFunction(this._Stream, this._WriteCallback); + OpenJpeg.StreamSetSeekFunction(this._Stream, this._SeekCallback); + OpenJpeg.StreamSetSkipFunction(this._Stream, this._SkipCallback); + + var compressionParameters = new CompressionParameters(); + OpenJpeg.SetDefaultEncoderParameters(compressionParameters); + compressionParameters.TcpNumLayers = 1; + compressionParameters.CodingParameterDistortionAllocation = 1; + + _Codec = OpenJpeg.CreateCompress(CodecFormat.J2k); + OpenJpeg.SetupEncoder(_Codec, compressionParameters, _Image); + } + + #endregion + + #region Properties + + public int Height + { + get; + private set; + } + + /// + /// Gets a value indicating whether this instance has been disposed. + /// + /// true if this instance has been disposed; otherwise, false. + public bool IsDisposed + { + get; + private set; + } + + public int Width + { + get; + private set; + } + + #endregion + + #region Methods + + public byte[] Encode() + { + OpenJpeg.StartCompress(_Codec, _Image, _Stream); + OpenJpeg.Encode(_Codec, _Stream); + OpenJpeg.EndCompress(_Codec, _Stream); + + var datast = Marshal.PtrToStructure(_UserData); + var output = new byte[datast.Position]; + Marshal.Copy(_Buffer.Data, output, 0, output.Length); + + return output; + } + + #region Event Handlers + + private static int Seek(ulong bytes, IntPtr userData) + { + unsafe + { + var buf = (J2KBuffer*)userData; + var position = Math.Min((ulong)buf->Length, bytes); + buf->Position = (int)position; + return 1; + } + } + + private static long Skip(ulong bytes, IntPtr userData) + { + unsafe + { + var buf = (J2KBuffer*)userData; + var bytesToSkip = (int)Math.Min((ulong)buf->Length, bytes); + if (bytesToSkip > 0) + { + buf->Position += bytesToSkip; + return bytesToSkip; + } + else + { + return unchecked(-1); + } + } + } + + private static ulong Write(IntPtr buffer, ulong bytes, IntPtr userData) + { + unsafe + { + var buf = (J2KBuffer*)userData; + var bytesToRead = (int)Math.Min((ulong)buf->Length, bytes); + if (bytesToRead > 0) + { + NativeMethods.cstd_memcpy(buffer, IntPtr.Add(buf->Data, buf->Position), bytesToRead); + buf->Position += bytesToRead; + return (ulong)bytesToRead; + } + else + { + return unchecked((ulong)-1); + } + } + } + + #endregion + + #region Helpers + + private CompressionParameters SetupEncoderParameters(OpenJpegDotNet.IO.Parameter parameter) + { + var compressionParameters = new CompressionParameters(); + OpenJpeg.SetDefaultEncoderParameters(compressionParameters); + + if (parameter.Compression.HasValue) + compressionParameters.TcpRates[0] = 1000f / Math.Min(Math.Max(parameter.Compression.Value, 1), 1000); + + compressionParameters.TcpNumLayers = 1; + compressionParameters.CodingParameterDistortionAllocation = 1; + + if (!parameter.Compression.HasValue) + compressionParameters.TcpRates[0] = 4; + + return compressionParameters; + } + + #endregion + + #endregion + + #region IDisposable Members + + /// + /// Releases all resources used by this . + /// + public void Dispose() + { + this.Dispose(true); + //GC.SuppressFinalize(this); + } + + /// + /// Releases all resources used by this . + /// + /// Indicate value whether method was called. + private void Dispose(bool disposing) + { + if (this.IsDisposed) + { + return; + } + + this.IsDisposed = true; + + if (disposing) + { + this._Codec?.Dispose(); + this._CompressionParameters?.Dispose(); + this._Stream.Dispose(); + + Marshal.FreeHGlobal(this._Buffer.Data); + Marshal.FreeHGlobal(this._UserData); + } + } + + #endregion + + } +} diff --git a/LibreMetaverse/Imaging/NativeMethods.cs b/LibreMetaverse/Imaging/NativeMethods.cs new file mode 100644 index 00000000..8cbc5403 --- /dev/null +++ b/LibreMetaverse/Imaging/NativeMethods.cs @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2021, Sjofn LLC. + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.co nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Runtime.InteropServices; + +namespace LibreMetaverse.Imaging +{ + internal sealed partial class NativeMethods + { + + #region Fields + + // Native library file name. + // If Linux, it will be converted to libOpenJpegDotNetNative.so + // If MacOSX, it will be converted to libOpenJpegDotNetNative.dylib + // If Windows, it will be available after call LoadLibrary. + // And this file name must not contain period. If it does, + // CLR does not add extension (.dll) and CLR fails to load library + internal const string NativeLibrary = "OpenJpegDotNetNative"; + + public const CallingConvention CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl; + + private static readonly WindowsLibraryLoader WindowsLibraryLoader = new WindowsLibraryLoader(); + + #endregion + + #region Constructors + + static NativeMethods() + { + WindowsLibraryLoader.LoadLibraries(new[] + { + $"{NativeLibrary}" + }); + } + + #endregion + + #region cstd + + [DllImport(NativeLibrary, CallingConvention = CallingConvention)] + public static extern IntPtr cstd_memcpy(IntPtr dest, IntPtr src, int count); + + #endregion + + } +} diff --git a/LibreMetaverse/Imaging/OpenJPEG.cs b/LibreMetaverse/Imaging/OpenJPEG.cs deleted file mode 100644 index b687f9fe..00000000 --- a/LibreMetaverse/Imaging/OpenJPEG.cs +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (c) 2006-2016, openmetaverse.co - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.co nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.Runtime.InteropServices; -using Rectangle = System.Drawing.Rectangle; - -namespace OpenMetaverse.Imaging -{ -#if !NO_UNSAFE - /// - /// A Wrapper around openjpeg to encode and decode images to and from byte arrays - /// - public class OpenJPEG - { - /// TGA Header size - public const int TGA_HEADER_SIZE = 32; - - #region JPEG2000 Structs - - /// - /// Defines the beginning and ending file positions of a layer in an - /// LRCP-progression JPEG2000 file - /// - [System.Diagnostics.DebuggerDisplay("Start = {Start} End = {End} Size = {End - Start}")] - [StructLayout(LayoutKind.Sequential, Pack = 4)] - public struct J2KLayerInfo - { - public int Start; - public int End; - } - - /// - /// This structure is used to marshal both encoded and decoded images. - /// MUST MATCH THE STRUCT IN dotnet.h! - /// - [StructLayout(LayoutKind.Sequential, Pack = 4)] - private struct MarshalledImage - { - public IntPtr encoded; // encoded image data - public int length; // encoded image length - public int dummy; // padding for 64-bit alignment - - public IntPtr decoded; // decoded image, contiguous components - - public int width; // width of decoded image - public int height; // height of decoded image - public int layers; // layer count - public int resolutions; // resolution count - public int components; // component count - public int packet_count; // packet count - public IntPtr packets; // pointer to the packets array - } - - /// - /// Information about a single packet in a JPEG2000 stream - /// - [StructLayout(LayoutKind.Sequential, Pack = 4)] - private struct MarshalledPacket - { - /// Packet start position - public int start_pos; - /// Packet header end position - public int end_ph_pos; - /// Packet end position - public int end_pos; - - public override string ToString() - { - return String.Format("start_pos: {0} end_ph_pos: {1} end_pos: {2}", - start_pos, end_ph_pos, end_pos); - } - } - - #endregion JPEG2000 Structs - - #region Unmanaged Function Declarations - - - // allocate encoded buffer based on length field - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetAllocEncoded(ref MarshalledImage image); - - // allocate decoded buffer based on width and height fields - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetAllocDecoded(ref MarshalledImage image); - - // free buffers - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetFree(ref MarshalledImage image); - - // encode raw to jpeg2000 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetEncode(ref MarshalledImage image, bool lossless); - - // decode jpeg2000 to raw - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetDecode(ref MarshalledImage image); - - // decode jpeg2000 to raw, get jpeg2000 file info - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetDecodeWithInfo(ref MarshalledImage image); - - // invoke 64 bit openjpeg calls - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetAllocEncoded64(ref MarshalledImage image); - - // allocate decoded buffer based on width and height fields - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetAllocDecoded64(ref MarshalledImage image); - - // free buffers - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetFree64(ref MarshalledImage image); - - // encode raw to jpeg2000 - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetEncode64(ref MarshalledImage image, bool lossless); - - // decode jpeg2000 to raw - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetDecode64(ref MarshalledImage image); - - // decode jpeg2000 to raw, get jpeg2000 file info - [System.Security.SuppressUnmanagedCodeSecurity] - [DllImport("openjpeg-dotnet-x86_64.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern bool DotNetDecodeWithInfo64(ref MarshalledImage image); - #endregion Unmanaged Function Declarations - - /// OpenJPEG is not threadsafe, so this object is used to lock - /// during calls into unmanaged code - private static object OpenJPEGLock = new object(); - - /// - /// Encode a object into a byte array - /// - /// The object to encode - /// true to enable lossless conversion, only useful for small images ie: sculptmaps - /// A byte array containing the encoded Image object - public static byte[] Encode(ManagedImage image, bool lossless) - { - if ((image.Channels & ManagedImage.ImageChannels.Color) == 0 || - ((image.Channels & ManagedImage.ImageChannels.Bump) != 0 && (image.Channels & ManagedImage.ImageChannels.Alpha) == 0)) - throw new ArgumentException("JPEG2000 encoding is not supported for this channel combination"); - - byte[] encoded = null; - MarshalledImage marshalled = new MarshalledImage(); - - // allocate and copy to input buffer - marshalled.width = image.Width; - marshalled.height = image.Height; - marshalled.components = 3; - if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) marshalled.components++; - if ((image.Channels & ManagedImage.ImageChannels.Bump) != 0) marshalled.components++; - - lock (OpenJPEGLock) - { - - bool allocSuccess = (IntPtr.Size == 8) ? DotNetAllocDecoded64(ref marshalled) : DotNetAllocDecoded(ref marshalled); - - if (!allocSuccess) - throw new Exception("DotNetAllocDecoded failed"); - - int n = image.Width * image.Height; - - if ((image.Channels & ManagedImage.ImageChannels.Color) != 0) - { - Marshal.Copy(image.Red, 0, marshalled.decoded, n); - Marshal.Copy(image.Green, 0, (IntPtr)(marshalled.decoded.ToInt64() + n), n); - Marshal.Copy(image.Blue, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 2), n); - } - - if ((image.Channels & ManagedImage.ImageChannels.Alpha) != 0) Marshal.Copy(image.Alpha, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 3), n); - if ((image.Channels & ManagedImage.ImageChannels.Bump) != 0) Marshal.Copy(image.Bump, 0, (IntPtr)(marshalled.decoded.ToInt64() + n * 4), n); - - // codec will allocate output buffer - bool encodeSuccess = (IntPtr.Size == 8) ? DotNetEncode64(ref marshalled, lossless) : DotNetEncode(ref marshalled, lossless); - if (!encodeSuccess) - throw new Exception("DotNetEncode failed"); - - // copy output buffer - encoded = new byte[marshalled.length]; - Marshal.Copy(marshalled.encoded, encoded, 0, marshalled.length); - - // free buffers - if (IntPtr.Size == 8) - DotNetFree64(ref marshalled); - else - DotNetFree(ref marshalled); - } - - return encoded; - } - - /// - /// Encode a object into a byte array - /// - /// The object to encode - /// a byte array of the encoded image - public static byte[] Encode(ManagedImage image) - { - return Encode(image, false); - } - - /// - /// Decode JPEG2000 data to an and - /// - /// - /// JPEG2000 encoded data - /// ManagedImage object to decode to - /// Image object to decode to - /// True if the decode succeeds, otherwise false - public static bool DecodeToImage(byte[] encoded, out ManagedImage managedImage, out Image image) - { - managedImage = null; - image = null; - - if (DecodeToImage(encoded, out managedImage)) - { - try - { - image = managedImage.ExportBitmap(); - return true; - } - catch (Exception ex) - { - Logger.Log("Failed to export and load TGA data from decoded image", Helpers.LogLevel.Error, ex); - return false; - } - } - else - { - return false; - } - } - - /// - /// - /// - /// - /// - /// - public static bool DecodeToImage(byte[] encoded, out ManagedImage managedImage) - { - MarshalledImage marshalled = new MarshalledImage(); - - // Allocate and copy to input buffer - marshalled.length = encoded.Length; - - lock (OpenJPEGLock) - { - if (IntPtr.Size == 8) - DotNetAllocEncoded64(ref marshalled); - else - DotNetAllocEncoded(ref marshalled); - - Marshal.Copy(encoded, 0, marshalled.encoded, encoded.Length); - - // Codec will allocate output buffer - if (IntPtr.Size == 8) - DotNetDecode64(ref marshalled); - else - DotNetDecode(ref marshalled); - - int n = marshalled.width * marshalled.height; - - switch (marshalled.components) - { - case 1: // Grayscale - managedImage = new ManagedImage(marshalled.width, marshalled.height, - ManagedImage.ImageChannels.Color); - Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n); - Buffer.BlockCopy(managedImage.Red, 0, managedImage.Green, 0, n); - Buffer.BlockCopy(managedImage.Red, 0, managedImage.Blue, 0, n); - break; - - case 2: // Grayscale + alpha - managedImage = new ManagedImage(marshalled.width, marshalled.height, - ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha); - Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n); - Buffer.BlockCopy(managedImage.Red, 0, managedImage.Green, 0, n); - Buffer.BlockCopy(managedImage.Red, 0, managedImage.Blue, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Alpha, 0, n); - break; - - case 3: // RGB - managedImage = new ManagedImage(marshalled.width, marshalled.height, - ManagedImage.ImageChannels.Color); - Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n); - break; - - case 4: // RGBA - managedImage = new ManagedImage(marshalled.width, marshalled.height, - ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha); - Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 3)), managedImage.Alpha, 0, n); - break; - - case 5: // RGBAB - managedImage = new ManagedImage(marshalled.width, marshalled.height, - ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha | ManagedImage.ImageChannels.Bump); - Marshal.Copy(marshalled.decoded, managedImage.Red, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)n), managedImage.Green, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 2)), managedImage.Blue, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 3)), managedImage.Alpha, 0, n); - Marshal.Copy((IntPtr)(marshalled.decoded.ToInt64() + (long)(n * 4)), managedImage.Bump, 0, n); - break; - - default: - Logger.Log("Decoded image with unhandled number of components: " + marshalled.components, - Helpers.LogLevel.Error); - - if (IntPtr.Size == 8) - DotNetFree64(ref marshalled); - else - DotNetFree(ref marshalled); - - managedImage = null; - return false; - } - - if (IntPtr.Size == 8) - DotNetFree64(ref marshalled); - else - DotNetFree(ref marshalled); - } - - return true; - } - - /// - /// - /// - /// - /// - /// - /// - public static bool DecodeLayerBoundaries(byte[] encoded, out J2KLayerInfo[] layerInfo, out int components) - { - bool success = false; - layerInfo = null; - components = 0; - MarshalledImage marshalled = new MarshalledImage(); - - // Allocate and copy to input buffer - marshalled.length = encoded.Length; - - lock (OpenJPEGLock) - { - if (IntPtr.Size == 8) - DotNetAllocEncoded64(ref marshalled); - else - DotNetAllocEncoded(ref marshalled); - - Marshal.Copy(encoded, 0, marshalled.encoded, encoded.Length); - - // Run the decode - bool decodeSuccess = (IntPtr.Size == 8) ? DotNetDecodeWithInfo64(ref marshalled) : DotNetDecodeWithInfo(ref marshalled); - if (decodeSuccess) - { - components = marshalled.components; - - // Sanity check - if (marshalled.layers * marshalled.resolutions * marshalled.components == marshalled.packet_count) - { - // Manually marshal the array of opj_packet_info structs - MarshalledPacket[] packets = new MarshalledPacket[marshalled.packet_count]; - int offset = 0; - - for (int i = 0; i < marshalled.packet_count; i++) - { - MarshalledPacket packet; - packet.start_pos = Marshal.ReadInt32(marshalled.packets, offset); - offset += 4; - packet.end_ph_pos = Marshal.ReadInt32(marshalled.packets, offset); - offset += 4; - packet.end_pos = Marshal.ReadInt32(marshalled.packets, offset); - offset += 4; - //double distortion = (double)Marshal.ReadInt64(marshalled.packets, offset); - offset += 8; - - packets[i] = packet; - } - - layerInfo = new J2KLayerInfo[marshalled.layers]; - - for (int i = 0; i < marshalled.layers; i++) - { - int packetsPerLayer = marshalled.packet_count / marshalled.layers; - MarshalledPacket startPacket = packets[packetsPerLayer * i]; - MarshalledPacket endPacket = packets[(packetsPerLayer * (i + 1)) - 1]; - layerInfo[i].Start = startPacket.start_pos; - layerInfo[i].End = endPacket.end_pos; - } - - // More sanity checking - if (layerInfo.Length == 0 || layerInfo[layerInfo.Length - 1].End <= encoded.Length - 1) - { - success = true; - - for (int i = 0; i < layerInfo.Length; i++) - { - if (layerInfo[i].Start >= layerInfo[i].End || - (i > 0 && layerInfo[i].Start <= layerInfo[i - 1].End)) - { - System.Text.StringBuilder output = new System.Text.StringBuilder( - "Inconsistent packet data in JPEG2000 stream:\n"); - for (int j = 0; j < layerInfo.Length; j++) - output.AppendFormat("Layer {0}: Start: {1} End: {2}\n", j, layerInfo[j].Start, layerInfo[j].End); - Logger.DebugLog(output.ToString()); - - success = false; - break; - } - } - - if (!success) - { - for (int i = 0; i < layerInfo.Length; i++) - { - if (i < layerInfo.Length - 1) - layerInfo[i].End = layerInfo[i + 1].Start - 1; - else - layerInfo[i].End = marshalled.length; - } - - Logger.DebugLog("Corrected JPEG2000 packet data"); - success = true; - - for (int i = 0; i < layerInfo.Length; i++) - { - if (layerInfo[i].Start >= layerInfo[i].End || - (i > 0 && layerInfo[i].Start <= layerInfo[i - 1].End)) - { - System.Text.StringBuilder output = new System.Text.StringBuilder( - "Still inconsistent packet data in JPEG2000 stream, giving up:\n"); - for (int j = 0; j < layerInfo.Length; j++) - output.AppendFormat("Layer {0}: Start: {1} End: {2}\n", j, layerInfo[j].Start, layerInfo[j].End); - Logger.DebugLog(output.ToString()); - - success = false; - break; - } - } - } - } - else - { - Logger.Log(String.Format( - "Last packet end in JPEG2000 stream extends beyond the end of the file. filesize={0} layerend={1}", - encoded.Length, layerInfo[layerInfo.Length - 1].End), Helpers.LogLevel.Warning); - } - } - else - { - Logger.Log(String.Format( - "Packet count mismatch in JPEG2000 stream. layers={0} resolutions={1} components={2} packets={3}", - marshalled.layers, marshalled.resolutions, marshalled.components, marshalled.packet_count), - Helpers.LogLevel.Warning); - } - } - - if (IntPtr.Size == 8) - DotNetFree64(ref marshalled); - else - DotNetFree(ref marshalled); - } - - return success; - } - - /// - /// Encode a object into a byte array - /// - /// The source object to encode - /// true to enable lossless decoding - /// A byte array containing the source Bitmap object - public unsafe static byte[] EncodeFromImage(Bitmap bitmap, bool lossless) - { - BitmapData bd; - ManagedImage decoded; - - int bitmapWidth = bitmap.Width; - int bitmapHeight = bitmap.Height; - int pixelCount = bitmapWidth * bitmapHeight; - int i; - - if ((bitmap.PixelFormat & PixelFormat.Alpha) != 0 || (bitmap.PixelFormat & PixelFormat.PAlpha) != 0) - { - // Four layers, RGBA - decoded = new ManagedImage(bitmapWidth, bitmapHeight, - ManagedImage.ImageChannels.Color | ManagedImage.ImageChannels.Alpha); - bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), - ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - byte* pixel = (byte*)bd.Scan0; - - for (i = 0; i < pixelCount; i++) - { - // GDI+ gives us BGRA and we need to turn that in to RGBA - decoded.Blue[i] = *(pixel++); - decoded.Green[i] = *(pixel++); - decoded.Red[i] = *(pixel++); - decoded.Alpha[i] = *(pixel++); - } - } - else if (bitmap.PixelFormat == PixelFormat.Format16bppGrayScale) - { - // One layer - decoded = new ManagedImage(bitmapWidth, bitmapHeight, - ManagedImage.ImageChannels.Color); - bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), - ImageLockMode.ReadOnly, PixelFormat.Format16bppGrayScale); - byte* pixel = (byte*)bd.Scan0; - - for (i = 0; i < pixelCount; i++) - { - // Normalize 16-bit data down to 8-bit - ushort origVal = (byte)(*(pixel) + (*(pixel + 1) << 8)); - byte val = (byte)(((double)origVal / (double)UInt32.MaxValue) * (double)Byte.MaxValue); - - decoded.Red[i] = val; - decoded.Green[i] = val; - decoded.Blue[i] = val; - pixel += 2; - } - } - else - { - // Three layers, RGB - decoded = new ManagedImage(bitmapWidth, bitmapHeight, - ManagedImage.ImageChannels.Color); - bd = bitmap.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), - ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - byte* pixel = (byte*)bd.Scan0; - - for (i = 0; i < pixelCount; i++) - { - decoded.Blue[i] = *(pixel++); - decoded.Green[i] = *(pixel++); - decoded.Red[i] = *(pixel++); - } - } - - bitmap.UnlockBits(bd); - byte[] encoded = Encode(decoded, lossless); - return encoded; - } - } -#endif -} diff --git a/LibreMetaverse/ImportExport/ColladalLoader.cs b/LibreMetaverse/ImportExport/ColladalLoader.cs index d3f5494a..110bd9a8 100644 --- a/LibreMetaverse/ImportExport/ColladalLoader.cs +++ b/LibreMetaverse/ImportExport/ColladalLoader.cs @@ -164,7 +164,10 @@ namespace OpenMetaverse.ImportExport bitmap = resized; } - material.TextureData = OpenJPEG.EncodeFromImage(bitmap, false); + using (var writer = new LibreMetaverse.Imaging.J2KWriter(bitmap)) + { + material.TextureData = writer.Encode(); + } Logger.Log("Successfully encoded " + fname, Helpers.LogLevel.Info); } diff --git a/LibreMetaverse/LibreMetaverse.csproj b/LibreMetaverse/LibreMetaverse.csproj index 0925d89b..3e59cd34 100644 --- a/LibreMetaverse/LibreMetaverse.csproj +++ b/LibreMetaverse/LibreMetaverse.csproj @@ -14,6 +14,7 @@ LibreMetaverse true netstandard2.0;netstandard2.1;net50 + AnyCPU;x64;x86 True @@ -32,6 +33,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -50,10 +85,47 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + LibreMetaverse.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + LibreMetaverse.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + diff --git a/LibreMetaverse/WindowsLibraryLoader.cs b/LibreMetaverse/WindowsLibraryLoader.cs new file mode 100644 index 00000000..f4aeacfc --- /dev/null +++ b/LibreMetaverse/WindowsLibraryLoader.cs @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2021, Sjofn LLC. + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.co nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; + +namespace LibreMetaverse +{ + internal sealed class WindowsLibraryLoader + { + #region Fields + + private const string ProcessorArchitecture = "PROCESSOR_ARCHITECTURE"; + private const string DllFileExtension = ".dll"; + private const string DllDirectory = "dll"; + private readonly Dictionary _ProcessorArchitectureAddressWidthPlatforms = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {"x86", 4}, + {"AMD64", 8}, + {"IA64", 8}, + {"ARM", 4} + }; + + private readonly Dictionary _ProcessorArchitecturePlatforms = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {"x86", "x86"}, + {"AMD64", "x64"}, + {"IA64", "Itanium"}, + {"ARM", "WinCE"} + }; + + private readonly object _SyncLock = new object(); + private static readonly IDictionary LoadedLibraries = new Dictionary(); + + [DllImport("kernel32", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)] + private static extern IntPtr Win32LoadLibrary(string dllPath); + + #endregion + + #region Properties + + public static bool IsCurrentPlatformSupported() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } + + public static bool IsWindows() + { + return Environment.OSVersion.Platform == PlatformID.Win32NT || + Environment.OSVersion.Platform == PlatformID.Win32S || + Environment.OSVersion.Platform == PlatformID.Win32Windows || + Environment.OSVersion.Platform == PlatformID.WinCE; + } + + #endregion + + #region Methods + + #region Helpers + + private static string FixUpDllFileName(string fileName) + { + if (string.IsNullOrEmpty(fileName)) + return fileName; + + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return fileName; + + if (!fileName.EndsWith(DllFileExtension, StringComparison.OrdinalIgnoreCase)) + return $"{fileName}{DllFileExtension}"; + + return fileName; + } + + private ProcessArchitectureInfo GetProcessArchitecture() + { + // BUG: Will this always be reliable? + var processArchitecture = Environment.GetEnvironmentVariable(ProcessorArchitecture); + var processInfo = new ProcessArchitectureInfo(); + if (!string.IsNullOrEmpty(processArchitecture)) + { + // Sanity check + processInfo.Architecture = processArchitecture; + } + else + { + processInfo.AddWarning("Failed to detect processor architecture, falling back to x86."); + processInfo.Architecture = (IntPtr.Size == 8) ? "x64" : "x86"; + } + + var addressWidth = this._ProcessorArchitectureAddressWidthPlatforms[processInfo.Architecture]; + if (addressWidth != IntPtr.Size) + { + if (String.Equals(processInfo.Architecture, "AMD64", StringComparison.OrdinalIgnoreCase) && IntPtr.Size == 4) + { + // fall back to x86 if detected x64 but has an address width of 32 bits. + processInfo.Architecture = "x86"; + processInfo.AddWarning("Expected the detected processing architecture of {0} to have an address width of {1} Bytes but was {2} Bytes, falling back to x86.", processInfo.Architecture, addressWidth, IntPtr.Size); + } + else + { + // no fallback possible + processInfo.AddWarning("Expected the detected processing architecture of {0} to have an address width of {1} Bytes but was {2} Bytes.", processInfo.Architecture, addressWidth, IntPtr.Size); + + } + } + + return processInfo; + } + + private string GetPlatformName(string processorArchitecture) + { + if (String.IsNullOrEmpty(processorArchitecture)) + return null; + + string platformName; + if (this._ProcessorArchitecturePlatforms.TryGetValue(processorArchitecture, out platformName)) + { + return platformName; + } + + return null; + } + + public void LoadLibraries(IEnumerable dlls) + { + if (!IsWindows()) + return; + + foreach (var dll in dlls) + LoadLibrary(dll); + } + + private void LoadLibrary(string dllName) + { + if (!IsCurrentPlatformSupported()) + return; + + try + { + lock (this._SyncLock) + { + if (LoadedLibraries.ContainsKey(dllName)) + return; + + var processArch = GetProcessArchitecture(); + IntPtr dllHandle; + + // Try loading from executing assembly domain + var executingAssembly = GetType().GetTypeInfo().Assembly; + var baseDirectory = Path.GetDirectoryName(executingAssembly.Location); + dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); + if (dllHandle != IntPtr.Zero) return; + + // Gets the pathname of the base directory that the assembly resolver uses to probe for assemblies. + // https://github.com/dotnet/corefx/issues/2221 + baseDirectory = AppContext.BaseDirectory; + dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); + if (dllHandle != IntPtr.Zero) return; + + // Finally try the working directory + baseDirectory = Path.GetFullPath(Directory.GetCurrentDirectory()); + dllHandle = LoadLibraryInternal(dllName, baseDirectory, processArch); + if (dllHandle != IntPtr.Zero) return; + + var errorMessage = new StringBuilder(); + errorMessage.Append($"Failed to find dll \"{dllName}\", for processor architecture {processArch.Architecture}."); + if (processArch.HasWarnings) + { + // include process detection warnings + errorMessage.Append($"\r\nWarnings: \r\n{processArch.WarningText()}"); + } + + throw new Exception(errorMessage.ToString()); + } + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + } + } + + private IntPtr LoadLibraryInternal(string dllName, string baseDirectory, ProcessArchitectureInfo processArchInfo) + { + var platformName = GetPlatformName(processArchInfo.Architecture); + var expectedDllDirectory = Path.Combine( + Path.Combine(baseDirectory, DllDirectory), platformName); + return this.LoadLibraryRaw(dllName, expectedDllDirectory); + } + + private IntPtr LoadLibraryRaw(string dllName, string baseDirectory) + { + var libraryHandle = IntPtr.Zero; + var fileName = FixUpDllFileName(Path.Combine(baseDirectory, dllName)); + + // Show where we're trying to load the file from + Debug.WriteLine($"Trying to load native library \"{fileName}\"..."); + + if (File.Exists(fileName)) + { + // Attempt to load dll + try + { + libraryHandle = Win32LoadLibrary(fileName); + if (libraryHandle != IntPtr.Zero) + { + // library has been loaded + Debug.WriteLine($"Successfully loaded native library \"{fileName}\"."); + LoadedLibraries.Add(dllName, libraryHandle); + } + else + { + Debug.WriteLine($"Failed to load native library \"{fileName}\".\r\nCheck windows event log."); + } + } + catch (Exception e) + { + var lastError = Marshal.GetLastWin32Error(); + Debug.WriteLine($"Failed to load native library \"{fileName}\".\r\nLast Error:{lastError}\r\nCheck inner exception and\\or windows event log.\r\nInner Exception: {e}"); + } + } + else + { + Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "The native library \"{0}\" does not exist.", fileName)); + } + + return libraryHandle; + } + + #endregion + + #endregion + + private class ProcessArchitectureInfo + { + #region Constructors + + public ProcessArchitectureInfo() + { + this.Warnings = new List(); + } + + #endregion + + #region Properties + + public string Architecture + { + get; set; + } + + private List Warnings + { + get; + } + + #endregion + + #region Methods + + public void AddWarning(string format, params object[] args) + { + Warnings.Add(String.Format(format, args)); + } + + public bool HasWarnings => Warnings.Count > 0; + + public string WarningText() + { + return string.Join("\r\n", Warnings.ToArray()); + } + + #endregion + } + } +} diff --git a/LibreMetaverseTypes/LibreMetaverse.Types.csproj b/LibreMetaverseTypes/LibreMetaverse.Types.csproj index 529950b5..0c77d7ae 100644 --- a/LibreMetaverseTypes/LibreMetaverse.Types.csproj +++ b/LibreMetaverseTypes/LibreMetaverse.Types.csproj @@ -14,6 +14,7 @@ Library LibreMetaverse true + AnyCPU;x64;x86 True @@ -32,6 +33,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -50,6 +85,42 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + LibreMetaverseTypes.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + LibreMetaverseTypes.XML + False + 4096 + True + ..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/PrimMesher/LibreMetaverse.PrimMesher.csproj b/PrimMesher/LibreMetaverse.PrimMesher.csproj index 3fe00fcf..6ddbd2c1 100644 --- a/PrimMesher/LibreMetaverse.PrimMesher.csproj +++ b/PrimMesher/LibreMetaverse.PrimMesher.csproj @@ -17,6 +17,7 @@ Copyright © OpenMetaverse Developers 2008, 2017. Copyright © Sjofn LLC 2018-2021. All rights reserved. LICENSE.txt https://github.com/cinderblocks/libremetaverse + AnyCPU;x64;x86 true @@ -27,6 +28,24 @@ prompt 4 + + true + full + false + ..\bin\ + TRACE;DEBUG;VERTEX_INDEXER + prompt + 4 + + + true + full + false + ..\bin\ + TRACE;DEBUG;VERTEX_INDEXER + prompt + 4 + pdbonly true @@ -35,6 +54,22 @@ prompt 4 + + pdbonly + true + ..\bin\ + TRACE;VERTEX_INDEXER + prompt + 4 + + + pdbonly + true + ..\bin\ + TRACE;VERTEX_INDEXER + prompt + 4 + diff --git a/Programs/Baker/Baker.csproj b/Programs/Baker/Baker.csproj index 446e5bcf..3e2fbc97 100644 --- a/Programs/Baker/Baker.csproj +++ b/Programs/Baker/Baker.csproj @@ -23,6 +23,7 @@ False + AnyCPU;x64;x86 diff --git a/Programs/GridProxy/GridProxy.csproj b/Programs/GridProxy/GridProxy.csproj index 7ea00e54..b008b54a 100644 --- a/Programs/GridProxy/GridProxy.csproj +++ b/Programs/GridProxy/GridProxy.csproj @@ -14,6 +14,7 @@ GridProxy true netcoreapp3.1;net5 + AnyCPU;x64;x86 True @@ -32,6 +33,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE;DEBUG + True + 4096 + False + ..\..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + True 285212672 @@ -49,6 +84,40 @@ 1591,1574,0419 AnyCPU + + True + 285212672 + False + TRACE + False + 4096 + True + ..\..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + + + True + 285212672 + False + TRACE + False + 4096 + True + ..\..\bin\ + False + False + False + 4 + False + 1591,1574,0419 + AnyCPU + diff --git a/Programs/VoiceTest/VoiceTest.csproj b/Programs/VoiceTest/VoiceTest.csproj index 1ed2fb74..0d4efb79 100644 --- a/Programs/VoiceTest/VoiceTest.csproj +++ b/Programs/VoiceTest/VoiceTest.csproj @@ -25,6 +25,7 @@ netcoreapp3.1;net50 + AnyCPU;x64;x86 diff --git a/Programs/examples/IRCGateway/IRCGateway.csproj b/Programs/examples/IRCGateway/IRCGateway.csproj index 1ef8ddaa..cff458cc 100644 --- a/Programs/examples/IRCGateway/IRCGateway.csproj +++ b/Programs/examples/IRCGateway/IRCGateway.csproj @@ -25,6 +25,7 @@ netcoreapp3.1;net5 + AnyCPU;x64;x86 diff --git a/Programs/examples/PacketDump/PacketDump.csproj b/Programs/examples/PacketDump/PacketDump.csproj index 8ae634ed..6f791584 100644 --- a/Programs/examples/PacketDump/PacketDump.csproj +++ b/Programs/examples/PacketDump/PacketDump.csproj @@ -25,6 +25,7 @@ netcoreapp3.1 + AnyCPU;x64;x86 diff --git a/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs b/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs index 26bcf6f3..0fc152a9 100644 --- a/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/DumpOutfitCommand.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using OpenMetaverse; using OpenMetaverse.Imaging; using OpenMetaverse.Assets; +using LibreMetaverse.Imaging; namespace OpenMetaverse.TestClient { @@ -91,10 +92,14 @@ namespace OpenMetaverse.TestClient File.WriteAllBytes(assetTexture.AssetID + ".jp2", assetTexture.AssetData); Console.WriteLine("Wrote JPEG2000 image " + assetTexture.AssetID + ".jp2"); - ManagedImage imgData; - OpenJPEG.DecodeToImage(assetTexture.AssetData, out imgData); - byte[] tgaFile = imgData.ExportTGA(); - File.WriteAllBytes(assetTexture.AssetID + ".tga", tgaFile); + using (J2KReader reader = new J2KReader(assetTexture.AssetData)) + { + reader.ReadHeader(); + System.Drawing.Bitmap bitmap = reader.DecodeToBitmap(); + ManagedImage imgData = new ManagedImage(bitmap); + byte[] tgaFile = imgData.ExportTGA(); + File.WriteAllBytes(assetTexture.AssetID + ".tga", tgaFile); + } Console.WriteLine("Wrote TGA image " + assetTexture.AssetID + ".tga"); } catch (Exception e) diff --git a/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs b/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs index 28fe8478..de21ef1d 100644 --- a/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs +++ b/Programs/examples/TestClient/Commands/Inventory/UploadImageCommand.cs @@ -77,7 +77,7 @@ namespace OpenMetaverse.TestClient private byte[] LoadImage(string fileName) { - byte[] UploadData; + byte[] UploadData = { }; string lowfilename = fileName.ToLower(); Bitmap bitmap = null; @@ -85,22 +85,22 @@ namespace OpenMetaverse.TestClient { if (lowfilename.EndsWith(".jp2") || lowfilename.EndsWith(".j2c")) { - Image image; - ManagedImage managedImage; - // Upload JPEG2000 images untouched UploadData = System.IO.File.ReadAllBytes(fileName); - - OpenJPEG.DecodeToImage(UploadData, out managedImage, out image); - bitmap = (Bitmap)image; + + using (var reader = new LibreMetaverse.Imaging.J2KReader(UploadData)) + { + reader.ReadHeader(); + bitmap = reader.DecodeToBitmap(); + } } else { - if (lowfilename.EndsWith(".tga")) + if (lowfilename.EndsWith(".tga")) { bitmap = LoadTGAClass.LoadTGA(fileName); - else + } else { bitmap = (Bitmap)Image.FromFile(fileName); - + } int oldwidth = bitmap.Width; int oldheight = bitmap.Height; @@ -137,8 +137,10 @@ namespace OpenMetaverse.TestClient bitmap.Dispose(); bitmap = resized; } - - UploadData = OpenJPEG.EncodeFromImage(bitmap, false); + using (var writer = new LibreMetaverse.Imaging.J2KWriter(bitmap)) + { + UploadData = writer.Encode(); + } } } catch (Exception ex) diff --git a/Programs/examples/TestClient/TestClient.csproj b/Programs/examples/TestClient/TestClient.csproj index c4e276aa..2aceae33 100644 --- a/Programs/examples/TestClient/TestClient.csproj +++ b/Programs/examples/TestClient/TestClient.csproj @@ -24,6 +24,7 @@ false netcoreapp3.1;net50 + AnyCPU;x64;x86 diff --git a/Programs/mapgenerator/mapgenerator.csproj b/Programs/mapgenerator/mapgenerator.csproj index 39880496..383be7cc 100644 --- a/Programs/mapgenerator/mapgenerator.csproj +++ b/Programs/mapgenerator/mapgenerator.csproj @@ -26,6 +26,7 @@ + AnyCPU;x64;x86 diff --git a/bin/OpenMetaverse.dll.config b/bin/OpenMetaverse.dll.config deleted file mode 100755 index ccafc0ff..00000000 --- a/bin/OpenMetaverse.dll.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/bin/Tao.OpenGl.dll.config b/bin/Tao.OpenGl.dll.config deleted file mode 100644 index a788d0f0..00000000 --- a/bin/Tao.OpenGl.dll.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-i686.so b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-i686.so deleted file mode 100755 index 193eca4b..00000000 Binary files a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-i686.so and /dev/null differ diff --git a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-x86_64.so b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-x86_64.so deleted file mode 100755 index 7a9bdfcd..00000000 Binary files a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-x86_64.so and /dev/null differ diff --git a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1.dylib b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1.dylib deleted file mode 100755 index 91f7264f..00000000 Binary files a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1.dylib and /dev/null differ diff --git a/bin/openjpeg-dotnet-x86_64.dll b/bin/openjpeg-dotnet-x86_64.dll deleted file mode 100755 index 9e8cd215..00000000 Binary files a/bin/openjpeg-dotnet-x86_64.dll and /dev/null differ diff --git a/bin/openjpeg-dotnet.dll b/bin/openjpeg-dotnet.dll deleted file mode 100755 index 6377b8d9..00000000 Binary files a/bin/openjpeg-dotnet.dll and /dev/null differ