diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a1c33a014..5a8cbabd6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -181,7 +181,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ 'windows-2019', 'ubuntu-latest', 'macos-latest' ] + os: [ 'ubuntu-latest', 'macos-latest' ] runs-on: ${{ matrix.os }} steps: - name: Checkout diff --git a/Examples/Complete/Camera/Blazor/Main.cs b/Examples/Complete/Camera/Blazor/Main.cs index b8386cd14f..e291921838 100644 --- a/Examples/Complete/Camera/Blazor/Main.cs +++ b/Examples/Complete/Camera/Blazor/Main.cs @@ -7,11 +7,7 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -83,105 +79,6 @@ public override void Run() return Path.GetExtension(id).Contains("fus", System.StringComparison.OrdinalIgnoreCase); } }); - - // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); - AssetStorage.RegisterProvider(fap); #endregion diff --git a/Examples/Complete/Deferred/Blazor/Main.cs b/Examples/Complete/Deferred/Blazor/Main.cs index 7a5b4a1d82..7b5106673a 100644 --- a/Examples/Complete/Deferred/Blazor/Main.cs +++ b/Examples/Complete/Deferred/Blazor/Main.cs @@ -7,11 +7,7 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -86,105 +82,6 @@ public override void Run() return Path.GetExtension(id).Contains("fus", System.StringComparison.OrdinalIgnoreCase); } }); - - // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); - AssetStorage.RegisterProvider(fap); #endregion diff --git a/Examples/Complete/Picking/Blazor/Main.cs b/Examples/Complete/Picking/Blazor/Main.cs index 0090381096..bd788436b1 100644 --- a/Examples/Complete/Picking/Blazor/Main.cs +++ b/Examples/Complete/Picking/Blazor/Main.cs @@ -7,11 +7,7 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -86,105 +82,6 @@ public override void Run() return Path.GetExtension(id).Contains("fus", System.StringComparison.OrdinalIgnoreCase); } }); - - // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); - AssetStorage.RegisterProvider(fap); #endregion diff --git a/Examples/Complete/PickingRayCast/Blazor/Main.cs b/Examples/Complete/PickingRayCast/Blazor/Main.cs index 7cba346acf..907087af4e 100644 --- a/Examples/Complete/PickingRayCast/Blazor/Main.cs +++ b/Examples/Complete/PickingRayCast/Blazor/Main.cs @@ -7,11 +7,7 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -82,105 +78,6 @@ public override void Run() return Path.GetExtension(id).Contains("fus", System.StringComparison.OrdinalIgnoreCase); } }); - - // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); - AssetStorage.RegisterProvider(fap); #endregion diff --git a/Examples/Complete/RenderContextOnly/Blazor/Main.cs b/Examples/Complete/RenderContextOnly/Blazor/Main.cs index f84ab32d73..f5dcfc7b5b 100644 --- a/Examples/Complete/RenderContextOnly/Blazor/Main.cs +++ b/Examples/Complete/RenderContextOnly/Blazor/Main.cs @@ -7,11 +7,7 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -84,102 +80,7 @@ public override void Run() }); // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); AssetStorage.RegisterProvider(fap); diff --git a/Examples/Complete/Simple/Blazor/Main.cs b/Examples/Complete/Simple/Blazor/Main.cs index bbaa24e452..349a835ae7 100644 --- a/Examples/Complete/Simple/Blazor/Main.cs +++ b/Examples/Complete/Simple/Blazor/Main.cs @@ -7,11 +7,7 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -82,105 +78,6 @@ public override void Run() return Path.GetExtension(id).Contains("fus", System.StringComparison.OrdinalIgnoreCase); } }); - - // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); - AssetStorage.RegisterProvider(fap); #endregion diff --git a/Examples/Complete/UI/Core/Assets/9SliceSprites-4.jpg b/Examples/Complete/UI/Core/Assets/9SliceSprites-4.jpg deleted file mode 100644 index 2e577ccfe4..0000000000 Binary files a/Examples/Complete/UI/Core/Assets/9SliceSprites-4.jpg and /dev/null differ diff --git a/Examples/Complete/UI/Core/Assets/censored_79_16.png b/Examples/Complete/UI/Core/Assets/censored_79_16.png deleted file mode 100644 index d65677b635..0000000000 Binary files a/Examples/Complete/UI/Core/Assets/censored_79_16.png and /dev/null differ diff --git a/Examples/Complete/UI/Core/Assets/censored_rgb.jpg b/Examples/Complete/UI/Core/Assets/censored_rgb.jpg new file mode 100644 index 0000000000..a5669851fe Binary files /dev/null and b/Examples/Complete/UI/Core/Assets/censored_rgb.jpg differ diff --git a/Examples/Complete/UI/Core/Assets/censored_rgba.png b/Examples/Complete/UI/Core/Assets/censored_rgba.png new file mode 100644 index 0000000000..9d6393bc3f Binary files /dev/null and b/Examples/Complete/UI/Core/Assets/censored_rgba.png differ diff --git a/Examples/Complete/UI/Core/Assets/townmusicians.jpg b/Examples/Complete/UI/Core/Assets/townmusicians.jpg deleted file mode 100644 index b478cd1825..0000000000 Binary files a/Examples/Complete/UI/Core/Assets/townmusicians.jpg and /dev/null differ diff --git a/Examples/Complete/UI/Core/Assets/townmusicians_297x500.jpg b/Examples/Complete/UI/Core/Assets/townmusicians_297x500.jpg new file mode 100644 index 0000000000..caed97b617 Binary files /dev/null and b/Examples/Complete/UI/Core/Assets/townmusicians_297x500.jpg differ diff --git a/Examples/Complete/UI/Core/Assets/townmusicians_300x500.jpg b/Examples/Complete/UI/Core/Assets/townmusicians_300x500.jpg new file mode 100644 index 0000000000..a75a605947 Binary files /dev/null and b/Examples/Complete/UI/Core/Assets/townmusicians_300x500.jpg differ diff --git a/Examples/Complete/UI/Core/UI.cs b/Examples/Complete/UI/Core/UI.cs index c48acaa5bb..c7131274a0 100644 --- a/Examples/Complete/UI/Core/UI.cs +++ b/Examples/Complete/UI/Core/UI.cs @@ -174,7 +174,7 @@ private SceneContainer CreateNineSliceScene() var nineSliceTextureNode = TextureNode.Create( "testImage", - new Texture(AssetStorage.Get("9SliceSprites-4.jpg")), + new Texture(AssetStorage.Get("9SliceSprites-4.png")), //In this setup the element will stay in the upper right corner of the parent and will not be stretched at all. GuiElementPosition.GetAnchors(AnchorPos.TopTopRight),//Anchor is in the upper right corner.//Anchor is in the upper right corner. @@ -384,9 +384,9 @@ public override void Init() _fontMap = new FontMap(fontLato, 24); - _bltDestinationTex = new Texture(AssetStorage.Get("townmusicians.jpg")); - var bltScrTex = new Texture(AssetStorage.Get("censored_79_16.png")); - _bltDestinationTex.Blt(180, 225, bltScrTex); + _bltDestinationTex = new Texture(AssetStorage.Get("townmusicians_297x500.jpg")); + var bltScrImgData = AssetStorage.Get("censored_rgba.png"); + _bltDestinationTex.Blt(210, 225, bltScrImgData); _btnCanvas = new GuiButton { diff --git a/src/Base/Common/GlyphInfo.cs b/src/Base/Common/GlyphInfo.cs new file mode 100644 index 0000000000..286b16a835 --- /dev/null +++ b/src/Base/Common/GlyphInfo.cs @@ -0,0 +1,67 @@ +namespace Fusee.Base.Common +{ + /// + /// A struct for saving character information needed for processing a font. + /// + public struct GlyphInfo + { + /// + /// The unicode character code this information is for. + /// + public uint CharCode; + + /// + /// The amount to advance the pen horizontally when drawing this glyph. + /// + public float AdvanceX; + + /// + /// The amount to advance the pen vertically when drawing this glyph. + /// Typically 0 for western fonts/script systems. + /// + public float AdvanceY; + + /// + /// The width of this glyph. + /// + public float Width; + + /// + /// The height of this glyph. + /// + public float Height; + + + /* These values have gone to FontMap in Engine.Core + /// + /// The width of this char on the texture atlas. + /// + public float BitmapW; + + /// + /// The height of this char on the texture atlas. + /// + public float BitmapH; + + /// + /// The left border of this char on the texture atlas. + /// + public float BitmapL; + + /// + /// The top border of this char on the texture atlas. + /// + public float BitmapT; + + /// + /// The x-offset of this char on the texture atlas. + /// + public float TexOffX; + + /// + /// The y-offset of this char on the texture atlas. + /// + public float TexOffY; + */ + } +} \ No newline at end of file diff --git a/src/Base/Common/IFontImp.cs b/src/Base/Common/IFontImp.cs deleted file mode 100644 index 9bc0982a4b..0000000000 --- a/src/Base/Common/IFontImp.cs +++ /dev/null @@ -1,145 +0,0 @@ -using Fusee.Math.Core; - -namespace Fusee.Base.Common -{ - /// - /// A struct for saving character information needed for processing a font. - /// - public struct GlyphInfo - { - /// - /// The unicode character code this information is for. - /// - public uint CharCode; - - /// - /// The amount to advance the pen horizontally when drawing this glyph. - /// - public float AdvanceX; - - /// - /// The amount to advance the pen vertically when drawing this glyph. - /// Typically 0 for western fonts/script systems. - /// - public float AdvanceY; - - /// - /// The width of this glyph. - /// - public float Width; - - /// - /// The height of this glyph. - /// - public float Height; - - - /* These values have gone to FontMap in Engine.Core - /// - /// The width of this char on the texture atlas. - /// - public float BitmapW; - - /// - /// The height of this char on the texture atlas. - /// - public float BitmapH; - - /// - /// The left border of this char on the texture atlas. - /// - public float BitmapL; - - /// - /// The top border of this char on the texture atlas. - /// - public float BitmapT; - - /// - /// The x-offset of this char on the texture atlas. - /// - public float TexOffX; - - /// - /// The y-offset of this char on the texture atlas. - /// - public float TexOffY; - */ - } - - /// - /// Common functionality that needs to be provided by a Font implementor. - /// - public interface IFontImp - { - /// - /// Gets and sets a value indicating whether the kerning definition of a font should be used. - /// - /// - /// true if the kerning definition of a font should be used; otherwise, false. - /// - bool UseKerning { get; set; } - - /// - /// Gets and sets the size in pixels. - /// - /// - /// The vertical size of the font in pixels. - /// - uint PixelHeight { get; set; } - - /// - /// Gets the character information. - /// - /// The character to retrieve information. - /// An information record about the character. - GlyphInfo GetGlyphInfo(uint c); - - /// - /// Translates the character's control points into a curve. - /// - /// The character from which the information is to be read. - /// - Curve GetGlyphCurve(uint c); - - /// - /// Get the unscaled advance from a character. - /// - /// The character from which the information is to be read. - /// - float GetUnscaledAdvance(uint c); - - /// - /// Renders the given glyph. - /// - /// The character code (Unicode) of the character to render. - /// - /// The x-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the left border of the image - /// to the leftmost pixel of the glyph within the rendered image. - /// - /// - /// The y-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the character's origin - /// (base line) of the image to the topmost pixel of the glyph within the rendered image. - /// - /// - /// An image data structure containing an image of the given character. - /// - IImageData RenderGlyph(uint c, out int bitmapLeft, out int bitmapTop); - - /// - /// Gets the kerning offset between a pair of two consecutive characters in a text string. - /// - /// The left character. - /// The right character. - /// An offset to add to the normal advance. Typically negative since kerning rather compacts text lines. - float GetKerning(uint leftC, uint rightC); - - /// - /// Gets the unscaled kerning offset between a pair of two consecutive characters in a text string. - /// - /// The left character. - /// The right character. - /// - float GetUnscaledKerning(uint leftC, uint rightC); - } -} \ No newline at end of file diff --git a/src/Base/Common/IImageBase.cs b/src/Base/Common/IImageBase.cs deleted file mode 100644 index 831c867954..0000000000 --- a/src/Base/Common/IImageBase.cs +++ /dev/null @@ -1,25 +0,0 @@ - -namespace Fusee.Base.Common -{ - /// - /// Collection of members, all types of images, e. g. ImageData and Textures, need to implement. - /// - public interface IImageBase - { - /// - /// Width in pixels. - /// - int Width { get; } - - /// - /// Height in pixels. - /// - int Height { get; } - - /// - /// Offers additional Information about the color format of the texture. - /// - ImagePixelFormat PixelFormat { get; } - - } -} \ No newline at end of file diff --git a/src/Base/Common/IImageData.cs b/src/Base/Common/IImageData.cs index a397143f2d..4a8916c160 100644 --- a/src/Base/Common/IImageData.cs +++ b/src/Base/Common/IImageData.cs @@ -5,8 +5,28 @@ namespace Fusee.Base.Common /// /// Interface describing operations that are possible on arbitrary image data types. /// - public interface IImageData : IImageBase + public interface IImageData { + /// + /// Width in pixels. + /// + int Width { get; } + + /// + /// Height in pixels. + /// + int Height { get; } + + /// + /// Offers additional Information about the color format of the texture. + /// + ImagePixelFormat PixelFormat { get; } + + /// + /// The pixel values. + /// + byte[] PixelData { get; } + /// /// Block Image Transfer. Write a block of pixels to this instance from some other IImageData /// diff --git a/src/Base/Core/Font.cs b/src/Base/Core/Font.cs index a3f7d316bb..b2e0415744 100644 --- a/src/Base/Core/Font.cs +++ b/src/Base/Core/Font.cs @@ -13,7 +13,7 @@ public class Font /// /// For implementation purposes only. Do not use this. /// - public IFontImp _fontImp; + public FontImpBase _fontImp; private readonly Dictionary _glyphInfoCache = new(); @@ -103,18 +103,10 @@ public float GetUnscaledAdvance(uint c) /// Renders the given glyph. /// /// The character code (Unicode) of the character to render. - /// - /// The x-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the left border of the image - /// to the leftmost pixel of the glyph within the rendered image. - /// - /// - /// The y-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the character's origin - /// (base line) of the image to the topmost pixel of the glyph within the rendered image. - /// /// /// An image data structure containing an image of the given character. /// - public IImageData RenderGlyph(uint c, out int bitmapLeft, out int bitmapTop) => _fontImp.RenderGlyph(c, out bitmapLeft, out bitmapTop); + public IImageData GetImageDataForGlyph(uint c, in GlyphInfo info) => _fontImp.GetImageDataForGlyph(c, in info); /// /// Gets the kerning offset between a pair of two consecutive characters in a text string. diff --git a/src/Base/Core/FontCore.cs b/src/Base/Core/FontImpBase.cs similarity index 65% rename from src/Base/Core/FontCore.cs rename to src/Base/Core/FontImpBase.cs index c3adaf235d..1ebf8049c5 100644 --- a/src/Base/Core/FontCore.cs +++ b/src/Base/Core/FontImpBase.cs @@ -1,6 +1,7 @@ -using Fusee.Base.Common; +using Fusee.Base.Common; using Fusee.Math.Core; using SixLabors.Fonts; +using SixLabors.Fonts.Unicode; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.PixelFormats; @@ -14,14 +15,8 @@ namespace Fusee.Base.Core /// /// Basic font implementation using Sixlabors.Fonts /// - public class FontCore : IFontImp + public abstract class FontImpBase { - /// - /// "delete" public ctor, prevent direct instancing of this class - /// - protected FontCore() - { } - /// /// The font instance generated from a font collection with only one font /// @@ -39,6 +34,10 @@ protected FontCore() /// public bool UseKerning { get; set; } = false; + /// + /// The current monitor's dots per inch. + /// + public int Dpi = 72; /// /// Gets and sets the currently used pixel height @@ -71,14 +70,15 @@ public Curve GetGlyphCurve(uint c) return curve; } - var glyph = _font.GetGlyph(Convert.ToChar(c)); + var glyph = _font.GetGlyphs(new CodePoint(c), ColorFontSupport.None).First(); + var outline = glyph.GlyphMetrics.GetOutline(); - var orgPointCoords = glyph.Instance.ControlPoints.ToArray(); - var pointTags = glyph.Instance.OnCurves.Select(x => x ? (byte)1 : (byte)0).ToArray(); + var orgPointCoords = outline.ControlPoints.ToArray(); + var pointTags = outline.OnCurves.ToArray().Select(x => x ? (byte)1 : (byte)0).ToArray(); if (orgPointCoords == null) return curve; // Freetype contours are defined by their end points. - var curvePartEndPoints = glyph.Instance.EndPoints.Select(x => (short)x).ToArray(); + var curvePartEndPoints = outline.EndPoints.ToArray().Select(x => (short)x).ToArray(); var partTags = new List(); var partVerts = new List(); @@ -111,17 +111,18 @@ public Curve GetGlyphCurve(uint c) /// public GlyphInfo GetGlyphInfo(uint c) { - GlyphInfo ret; + var glyph = _font.GetGlyphs(new CodePoint(c), ColorFontSupport.None).First(); - var options = new RendererOptions(_font); - FontRectangle size = TextMeasurer.Measure(Convert.ToChar(c).ToString(), options); + var scaledPointSize = _font.Size * Dpi; + var scaleFactor = scaledPointSize / glyph.GlyphMetrics.ScaleFactor; + GlyphInfo ret; ret.CharCode = c; - ret.AdvanceX = size.Width; - ret.AdvanceY = 0; + ret.AdvanceX = glyph.GlyphMetrics.AdvanceWidth * scaleFactor; + ret.AdvanceY = glyph.GlyphMetrics.AdvanceHeight * scaleFactor; - ret.Width = size.Width; - ret.Height = size.Height; + ret.Width = glyph.GlyphMetrics.Width * scaleFactor; + ret.Height = glyph.GlyphMetrics.Height * scaleFactor; return ret; } @@ -135,8 +136,9 @@ public GlyphInfo GetGlyphInfo(uint c) /// public float GetKerning(uint leftC, uint rightC) { - var offset = _font.Instance.GetOffset(_font.GetGlyph(Convert.ToChar(leftC)).Instance, _font.GetGlyph(Convert.ToChar(rightC)).Instance); - return offset.X; + //var glyphLeft = _font.GetGlyphs(new CodePoint(leftC), ColorFontSupport.None).First(); + //var glyphRight = _font.GetGlyphs(new CodePoint(rightC), ColorFontSupport.None).First(); + return 0; } /// @@ -146,8 +148,8 @@ public float GetKerning(uint leftC, uint rightC) /// public float GetUnscaledAdvance(uint c) { - var glyph = _font.Instance.GetGlyph(Convert.ToChar(c)); - return glyph.AdvanceWidth; + var glyphs = _font.GetGlyphs(new CodePoint(c), ColorFontSupport.None); + return glyphs.First().GlyphMetrics.AdvanceWidth; } /// @@ -158,73 +160,41 @@ public float GetUnscaledAdvance(uint c) /// public float GetUnscaledKerning(uint leftC, uint rightC) { - var offset = _font.Instance.GetOffset(_font.GetGlyph(Convert.ToChar(leftC)).Instance, _font.GetGlyph(Convert.ToChar(rightC)).Instance); - return offset.X; + var glyphLeft = _font.GetGlyphs(new CodePoint(leftC), ColorFontSupport.None).First(); + var glyphRight = _font.GetGlyphs(new CodePoint(rightC), ColorFontSupport.None).First(); + return 0; } /// /// Renders a glyph to an IImageData for further use /// /// - /// - /// The x-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the left border of the image - /// to the leftmost pixel of the glyph within the rendered image. - /// - /// - /// The y-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the character's origin - /// (base line) of the image to the topmost pixel of the glyph within the rendered image. - /// /// - public IImageData RenderGlyph(uint c, out int bitmapLeft, out int bitmapTop) + public IImageData GetImageDataForGlyph(uint c, in GlyphInfo info) { - var options = new RendererOptions(_font); - FontRectangle size = TextMeasurer.Measure(Convert.ToChar(c).ToString(), options); + var width = (int)System.Math.Ceiling(info.AdvanceX); + var height = (int)System.Math.Ceiling(info.AdvanceY); + var arr = new Rgba32[width * height]; + var res = new Span(arr); - var drawingOptions = new DrawingOptions - { - TextOptions = new TextOptions() - { - ApplyKerning = UseKerning, - DpiX = options.DpiX, - DpiY = options.DpiY, - TabWidth = options.TabWidth, - LineSpacing = options.LineSpacing, - HorizontalAlignment = options.HorizontalAlignment, - VerticalAlignment = options.VerticalAlignment, - WrapTextWidth = options.WrappingWidth, - RenderColorFonts = options.ColorFontSupport != ColorFontSupport.None - } - }; - - var width = (int)System.Math.Max(1, System.Math.Round(size.Width)); - var height = (int)System.Math.Max(1, size.Height); - Span res = null; - bitmapLeft = 0; - bitmapTop = 0; try { - using var img = CreateImage(drawingOptions, Convert.ToChar(c).ToString(), - options.Font, width, height, - options.Origin, Color.Black); - - bitmapLeft = (int)size.Left; - bitmapTop = -(int)size.Top; - - img.TryGetSinglePixelSpan(out res); + using var img = CreateImage(new DrawingOptions(), Convert.ToChar(c).ToString(), + _font, width, height, + new System.Numerics.Vector2(0, 0), Color.Black); + img.CopyPixelDataTo(res); } // invalid (unknown) chars - catch (Exception) + catch (Exception e) { Diagnostics.Warn($"Generating glyph for char {c}:{Convert.ToChar(c)} failed, skipping"); return new ImageData(0, 0); } var ret = new ImageData(res.ToArray().Select(x => x.A).ToArray(), width, height, new ImagePixelFormat(ColorFormat.Intensity)); - return ret; } - private static Image CreateImage(DrawingOptions options, string text, SixLabors.Fonts.Font font, @@ -241,12 +211,11 @@ private static Image CreateImage(DrawingOptions options, } } - internal static class SplitToCurvePartHelper { #region Methods - public static void CurvePartVertice(CurvePart cp, int j, System.Numerics.Vector2[] orgPointCoords, List partVerts) + public static void CurvePartVertice(int j, System.Numerics.Vector2[] orgPointCoords, List partVerts) { var vert = new float3(orgPointCoords[j].X, orgPointCoords[j].Y, 0); partVerts.Add(vert); @@ -266,7 +235,7 @@ public static CurvePart CreateCurvePart(System.Numerics.Vector2[] orgPointCoords { for (var j = 0; j <= i; j++) { - CurvePartVertice(cp, j, orgPointCoords, partVerts); + CurvePartVertice(j, orgPointCoords, partVerts); partTags.Add(pointTags[j]); } //The start point is the first point in the outline.Points array. @@ -278,7 +247,7 @@ public static CurvePart CreateCurvePart(System.Numerics.Vector2[] orgPointCoords { for (var j = curvePartEndPoints[index - 1] + 1; j <= curvePartEndPoints[index]; j++) { - CurvePartVertice(cp, j, orgPointCoords, partVerts); + CurvePartVertice(j, orgPointCoords, partVerts); partTags.Add(pointTags[j]); } diff --git a/src/Base/Core/Fusee.Base.Core.csproj b/src/Base/Core/Fusee.Base.Core.csproj index e1c13fcd67..0ca173a388 100644 --- a/src/Base/Core/Fusee.Base.Core.csproj +++ b/src/Base/Core/Fusee.Base.Core.csproj @@ -16,9 +16,10 @@ analyzers - - + + + diff --git a/src/Base/Core/ImageData.cs b/src/Base/Core/ImageData.cs index e645dddad8..1981746760 100644 --- a/src/Base/Core/ImageData.cs +++ b/src/Base/Core/ImageData.cs @@ -135,16 +135,15 @@ public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, ColorFormat.RGBA => src.PixelFormat.ColorFormat switch { ColorFormat.RGB => delegate (byte[] srcLineBytes, int destinationIndex) - { - for (int i = 0; i < srcLineBytes.Length; i += 3) // jump 3 units per loop because we want to copy src RGB to dst RGBA - { - PixelData[destinationIndex + i + 0] = srcLineBytes[i + 0]; - PixelData[destinationIndex + i + 1] = srcLineBytes[i + 1]; - PixelData[destinationIndex + i + 2] = srcLineBytes[i + 2]; - PixelData[destinationIndex + i + 3] = byte.MaxValue; - } - } - + { + for (int i = 0; i < srcLineBytes.Length; i += 3) // jump 3 units per loop because we want to copy src RGB to dst RGBA + { + PixelData[destinationIndex + i + 0] = srcLineBytes[i + 0]; + PixelData[destinationIndex + i + 1] = srcLineBytes[i + 1]; + PixelData[destinationIndex + i + 2] = srcLineBytes[i + 2]; + PixelData[destinationIndex + i + 3] = byte.MaxValue; + } + } , ColorFormat.Intensity => delegate (byte[] srcLineBytes, int destinationIndex) { @@ -156,22 +155,21 @@ public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, PixelData[destinationIndex + i + 3] = byte.MaxValue; } } - , _ => throw new ArgumentOutOfRangeException(nameof(src), "Unknown source pixel format to copy to RGBA"), }, ColorFormat.RGB => src.PixelFormat.ColorFormat switch { ColorFormat.RGBA => delegate (byte[] srcLineBytes, int destinationIndex) - { - for (int i = 0; i < srcLineBytes.Length; i += 4) // jump 4 units per loop because we want to copy src RGBA to dst RGB - { - PixelData[destinationIndex + i + 0] = srcLineBytes[i + 0]; - PixelData[destinationIndex + i + 1] = srcLineBytes[i + 1]; - PixelData[destinationIndex + i + 2] = srcLineBytes[i + 2]; - // skip source alpha - } - } + { + for (int i = 0; i < srcLineBytes.Length; i += 4) // jump 4 units per loop because we want to copy src RGBA to dst RGB + { + PixelData[destinationIndex + i + 0] = srcLineBytes[i + 0]; + PixelData[destinationIndex + i + 1] = srcLineBytes[i + 1]; + PixelData[destinationIndex + i + 2] = srcLineBytes[i + 2]; + // skip source alpha + } + } , ColorFormat.Intensity => delegate (byte[] srcLineBytes, int destinationIndex) @@ -183,25 +181,23 @@ public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, PixelData[destinationIndex + i + 2] = srcLineBytes[i]; } } - , _ => throw new ArgumentOutOfRangeException(nameof(src), "Unknown source pixel format to copy to RGB"), }, ColorFormat.Intensity => src.PixelFormat.ColorFormat switch { ColorFormat.RGB => delegate (byte[] srcLineBytes, int destinationIndex) - { - for (int i = 0; i < srcLineBytes.Length; i += 3) // jump 3 units per loop because we want to copy src RGB to dst Intensity - { - // Quick integer Luma conversion (not accurate) - // See http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color - int r = srcLineBytes[destinationIndex + i + 0]; - int g = srcLineBytes[destinationIndex + i + 1]; - int b = srcLineBytes[destinationIndex + i + 2]; - PixelData[destinationIndex + i] = (byte)((r + r + b + g + g + g) / 6); - } - } - + { + for (int i = 0; i < srcLineBytes.Length; i += 3) // jump 3 units per loop because we want to copy src RGB to dst Intensity + { + // Quick integer Luma conversion (not accurate) + // See http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color + int r = srcLineBytes[destinationIndex + i + 0]; + int g = srcLineBytes[destinationIndex + i + 1]; + int b = srcLineBytes[destinationIndex + i + 2]; + PixelData[destinationIndex + i] = (byte)((r + r + b + g + g + g) / 6); + } + } , ColorFormat.RGBA => delegate (byte[] srcLineBytes, int destinationIndex) { @@ -215,7 +211,6 @@ public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, PixelData[destinationIndex + i] = (byte)((r + r + b + g + g + g) / 6); } } - , _ => throw new ArgumentOutOfRangeException(nameof(src), "Unknown source pixel format to copy to RGB"), }, diff --git a/src/Base/Imp/Android/FontImp.cs b/src/Base/Imp/Android/FontImp.cs index 599e86dc92..3ee01e40e2 100644 --- a/src/Base/Imp/Android/FontImp.cs +++ b/src/Base/Imp/Android/FontImp.cs @@ -9,7 +9,7 @@ namespace Fusee.Base.Imp.Android /// /// Font implementation using SixLabors.Fonts /// - public class FontImp : FontCore + public class FontImp : FontImpBase { /// /// Font ctor implementation for Android @@ -23,7 +23,7 @@ public FontImp(Stream stream) stream.CopyTo(memoryStream); stream.Flush(); memoryStream.Position = 0; - _collection.Install(memoryStream); + _collection.Add(memoryStream); _font = _collection.Families.AsEnumerable().First().CreateFont(PixelHeight); } } diff --git a/src/Base/Imp/Android/Fusee.Base.Imp.Android.csproj b/src/Base/Imp/Android/Fusee.Base.Imp.Android.csproj index 759cb38ad6..1f7ee39ba5 100644 --- a/src/Base/Imp/Android/Fusee.Base.Imp.Android.csproj +++ b/src/Base/Imp/Android/Fusee.Base.Imp.Android.csproj @@ -16,9 +16,6 @@ analyzers - - - diff --git a/src/Base/Imp/Blazor/BlazorAssetProvider.cs b/src/Base/Imp/Blazor/BlazorAssetProvider.cs index e016aea753..5ed955cdb1 100644 --- a/src/Base/Imp/Blazor/BlazorAssetProvider.cs +++ b/src/Base/Imp/Blazor/BlazorAssetProvider.cs @@ -1,13 +1,9 @@ using Fusee.Base.Common; using Fusee.Base.Core; using Microsoft.JSInterop; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; using System.IO; using System.Net.Http; -using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -61,10 +57,42 @@ public AssetProvider(IJSRuntime runtime, string baseDir = null) if (_baseDir[^1] != '/') _baseDir += '/'; + // Image handler + RegisterTypeHandler(new AssetHandler + { + ReturnedType = typeof(ImageData), + Decoder = (string id, object storage) => + { + throw new NotSupportedException("Synchronous Decoder is not supported - use DecoderAsync instead!"); + }, + DecoderAsync = async (string id, object storage) => + { + var ext = Path.GetExtension(id).ToLower(); + return ext switch + { + ".jpg" or ".jpeg" or ".png" or ".bmp" or ".tga" => await FileDecoder.LoadImageAsync((Stream)storage).ConfigureAwait(false), + _ => null, + }; + }, + Checker = (string id) => + { + var ext = Path.GetExtension(id).ToLower(); + return ext switch + { + ".jpg" or ".jpeg" or ".png" or ".bmp" or ".tga" => true, + _ => false, + }; + } + }); + // Text file -> String handler. Keep this one the last entry as it doesn't check the extension RegisterTypeHandler(new AssetHandler { ReturnedType = typeof(string), + Decoder = (string id, object storage) => + { + throw new NotSupportedException("Synchronous Decoder is not supported - use DecoderAsync instead!"); + }, DecoderAsync = async (string _, object storage) => { Stream storageStream = (Stream)storage; @@ -165,79 +193,5 @@ protected override async Task CheckExistsAsync(string id) HttpResponseMessage response = await httpClient.GetAsync(id).ConfigureAwait(false); return response.StatusCode == System.Net.HttpStatusCode.OK; } - - /// - /// Loads an image from a given stream - /// - /// - /// - public static ImageData LoadImage(Stream file) - { - try - { - using Image image = Image.Load(file, out SixLabors.ImageSharp.Formats.IImageFormat imgFormat); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - - - int bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out Span res); - Span resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - Image rgb = image as Image; - Image bgr = rgb.CloneAs(); - - bgr.TryGetSinglePixelSpan(out Span res); - Span resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - Image rgba = image as Image; - Image bgra = rgba.CloneAs(); - - bgra.TryGetSinglePixelSpan(out Span res); - Span resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - Image rgba = image as Image; - Image bgra = rgba.CloneAs(); - - (image as Image).TryGetSinglePixelSpan(out Span res); - Span resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out Span res); - Span resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - } - } - catch (Exception ex) - { - Diagnostics.Error($"Error loading/converting image", ex); - return null; - } - } } } \ No newline at end of file diff --git a/src/Base/Imp/Blazor/FileDecoder.cs b/src/Base/Imp/Blazor/FileDecoder.cs new file mode 100644 index 0000000000..64666c8cc8 --- /dev/null +++ b/src/Base/Imp/Blazor/FileDecoder.cs @@ -0,0 +1,108 @@ +using Fusee.Base.Common; +using Fusee.Base.Core; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Fusee.Base.Imp.Blazor +{ + /// + /// Provides methods for platform specific conversions of file content. + /// + public static class FileDecoder + { + /// + /// Loads a new Bitmap-Object from the given stream. + /// + /// Stream containing the image in a supported format (png, jpg). + /// An ImageData object with all necessary information. + public static Task LoadImageAsync(Stream file) + { + return Task.FromResult(LoadImage(file)); + } + + /// + /// Loads a new Bitmap-Object from the given stream. + /// + /// Stream containing the image in a supported format (png, jpg). + /// An ImageData object with all necessary information. + public static ImageData LoadImage(Stream file) + { + try + { + using var ms = new MemoryStream(); + file.CopyTo(ms); + ms.Position = 0; + //Load the image + using var image = Image.Load(ms, out var imgFormat); + + //ImageSharp loads from the top-left pixel, whereas OpenGL loads from the bottom-left, causing the texture to be flipped vertically. + //This will correct that, making the texture display properly. + image.Mutate(x => x.Flip(FlipMode.Vertical)); + + var bitsPerPixel = image.PixelType.BitsPerPixel; + var bytesPerPixel = bitsPerPixel / 8; + //Convert ImageSharp's format into a byte array, so we can use it with OpenGL. + var pixels = new byte[bytesPerPixel * image.Width * image.Height]; + + ImageData img; + switch (bitsPerPixel) + { + case 16: + { + (image as Image).CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.Depth16)); + break; + } + case 24: + { + var rgb = image as Image; + + rgb.CopyPixelDataTo(pixels); + + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.RGB)); + break; + } + case 32: + { + var rgba = image as Image; + + rgba.CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.RGBA)); + break; + } + case 48: + { + (image as Image).CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.fRGB32)); + break; + } + case 64: + { + (image as Image).CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.fRGBA32)); + break; + } + default: + throw new ArgumentException($"{bitsPerPixel} Bits per pixel not supported!"); + } + + Array.Copy(pixels, img.PixelData, pixels.Length); + return img; + } + catch (Exception ex) + { + Diagnostics.Error($"Error loading/converting image", ex); + return new ImageData(); + } + } + } +} \ No newline at end of file diff --git a/src/Base/Imp/Blazor/FontImp.cs b/src/Base/Imp/Blazor/FontImp.cs index e0a4e41790..a3553e4248 100644 --- a/src/Base/Imp/Blazor/FontImp.cs +++ b/src/Base/Imp/Blazor/FontImp.cs @@ -1,27 +1,13 @@ -using Fusee.Base.Common; using Fusee.Base.Core; -using Fusee.Math.Core; using SixLabors.Fonts; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Drawing.Processing; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using Font = SixLabors.Fonts.Font; namespace Fusee.Base.Imp.Blazor { /// /// Font implementation using SixLabors.Fonts /// - public class FontImp : IFontImp + public class FontImp : FontImpBase { - internal Font _font; - internal FontCollection _collection; - /// /// Font implementation for WebAsm /// @@ -29,252 +15,7 @@ public class FontImp : IFontImp public FontImp(System.IO.Stream stream) { _collection = new FontCollection(); - _collection.Install(stream); - } - - /// - /// Use kerning - /// - public bool UseKerning { get; set; } = false; - - private uint _pixelHeight = 24; - - /// - /// Gets and sets the currently used pixel height - /// - public uint PixelHeight - { - get => _pixelHeight; - - set - { - _pixelHeight = value; - _font = _collection.Families.AsEnumerable().First().CreateFont(_pixelHeight); - } - } - - /// - /// Returns the glyph curve from a given char - /// - /// - public Curve GetGlyphCurve(uint c) - { - Curve curve = new() - { - CurveParts = new List() - }; - - // don't print space - if (c == 32) - { - return curve; - } - - Glyph glyph = _font.GetGlyph(Convert.ToChar(c)); - - Vector2[] orgPointCoords = glyph.Instance.ControlPoints.ToArray(); - byte[] pointTags = glyph.Instance.OnCurves.Select(x => x ? (byte)1 : (byte)0).ToArray(); - if (orgPointCoords == null) return curve; - - // Freetype contours are defined by their end points. - short[] curvePartEndPoints = glyph.Instance.EndPoints.Select(x => (short)x).ToArray(); - - List partTags = new(); - List partVerts = new(); - - //Writes points of a freetype contour into a CurvePart, - for (int i = 0; i <= orgPointCoords.Length; i++) - { - //If a certain index of outline points is in array of contour end points - create new CurvePart and add it to Curve.CurveParts - if (!curvePartEndPoints.ToList().Contains((short)i)) continue; - - partVerts.Clear(); - partTags.Clear(); - - CurvePart part = SplitToCurvePartHelper.CreateCurvePart(orgPointCoords, pointTags, curvePartEndPoints, i, - partVerts, partTags); - curve.CurveParts.Add(part); - - List segments = SplitToCurveSegmentHelper.SplitPartIntoSegments(part, partTags, partVerts); - SplitToCurveSegmentHelper.CombineCurveSegmentsAndAddThemToCurvePart(segments, part); - } - - return curve; - - } - - /// - /// Get glyph info from letter - /// - /// letter char - /// - public GlyphInfo GetGlyphInfo(uint c) - { - GlyphInfo ret; - - RendererOptions options = new RendererOptions(_font); - FontRectangle size = TextMeasurer.Measure(Convert.ToChar(c).ToString(), options); - - ret.CharCode = c; - ret.AdvanceX = size.Width; - ret.AdvanceY = 0; - - ret.Width = size.Width; - ret.Height = size.Height; - - return ret; - } - - - /// - /// Returns the kerning between two chars - /// - /// - /// - /// - public float GetKerning(uint leftC, uint rightC) - { - Vector2 offset = _font.Instance.GetOffset(_font.GetGlyph(Convert.ToChar(leftC)).Instance, _font.GetGlyph(Convert.ToChar(rightC)).Instance); - return offset.X; - } - - /// - /// Returns the unscaled advance of one glyph - /// - /// - /// - public float GetUnscaledAdvance(uint c) - { - GlyphInstance glyph = _font.Instance.GetGlyph(Convert.ToChar(c)); - return glyph.AdvanceWidth; - } - - /// - /// Returns the unscaled kerning, currently TODO - /// - /// - /// - /// - public float GetUnscaledKerning(uint leftC, uint rightC) - { - Vector2 offset = _font.Instance.GetOffset(_font.GetGlyph(Convert.ToChar(leftC)).Instance, _font.GetGlyph(Convert.ToChar(rightC)).Instance); - return offset.X; - } - - /// - /// Renders a glyph to an IImageData for further use - /// - /// - /// - /// The x-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the left border of the image - /// to the leftmost pixel of the glyph within the rendered image. - /// - /// - /// The y-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the character's origin - /// (base line) of the image to the topmost pixel of the glyph within the rendered image. - /// - /// - public IImageData RenderGlyph(uint c, out int bitmapLeft, out int bitmapTop) - { - RendererOptions options = new RendererOptions(_font); - FontRectangle size = TextMeasurer.Measure(Convert.ToChar(c).ToString(), options); - - DrawingOptions drawingOptions = new DrawingOptions - { - TextOptions = new TextOptions() - { - ApplyKerning = UseKerning, - DpiX = options.DpiX, - DpiY = options.DpiY, - TabWidth = options.TabWidth, - LineSpacing = options.LineSpacing, - HorizontalAlignment = options.HorizontalAlignment, - VerticalAlignment = options.VerticalAlignment, - WrapTextWidth = options.WrappingWidth, - RenderColorFonts = options.ColorFontSupport != ColorFontSupport.None - } - }; - - int width = (int)System.Math.Max(1, System.Math.Round(size.Width)); - int height = (int)System.Math.Max(1, size.Height); - - using Image img = CreateImage(drawingOptions, Convert.ToChar(c).ToString(), - options.Font, width, height, - options.Origin, Color.Black); - - bitmapLeft = (int)size.Left; - bitmapTop = -(int)size.Top; - - img.TryGetSinglePixelSpan(out Span res); - - ImageData ret = new(res.ToArray().Select(x => x.A).ToArray(), width, height, new ImagePixelFormat(ColorFormat.Intensity)); - - return ret; - } - - - private static Image CreateImage(DrawingOptions options, - string text, - Font font, - int width, - int height, - Vector2 origin, - Color color) - { - Image img = new Image(width, height); - img.Mutate(x => x.Fill(Color.FromRgba(0, 0, 0, 0))); - img.Mutate(x => x.DrawText(options, text, font, color, origin)); - - return img; - } - } - - - internal static class SplitToCurvePartHelper - { - #region Methods - - public static void CurvePartVertice(CurvePart cp, int j, Vector2[] orgPointCoords, List partVerts) - { - float3 vert = new(orgPointCoords[j].X, orgPointCoords[j].Y, 0); - partVerts.Add(vert); - } - - public static CurvePart CreateCurvePart(Vector2[] orgPointCoords, byte[] pointTags, short[] curvePartEndPoints, int i, List partVerts, List partTags) - { - int index = Array.IndexOf(curvePartEndPoints, (short)i); - CurvePart cp = new() - { - IsClosed = true, - CurveSegments = new List() - }; - - //Marginal case - first contour ( 0 to contours[0] ). - if (index == 0) - { - for (int j = 0; j <= i; j++) - { - CurvePartVertice(cp, j, orgPointCoords, partVerts); - partTags.Add(pointTags[j]); - } - //The start point is the first point in the outline.Points array. - cp.StartPoint = new float3(orgPointCoords[0].X, orgPointCoords[0].Y, 0); - } - - //contours[0]+1 to contours[1] - else - { - for (int j = curvePartEndPoints[index - 1] + 1; j <= curvePartEndPoints[index]; j++) - { - CurvePartVertice(cp, j, orgPointCoords, partVerts); - partTags.Add(pointTags[j]); - } - - //The index in outline.Points which describes the start point is given by the index of the foregone outline.contours index +1. - cp.StartPoint = new float3(orgPointCoords[curvePartEndPoints[index - 1] + 1].X, orgPointCoords[curvePartEndPoints[index - 1] + 1].Y, 0); - } - return cp; + _collection.Add(stream); } - #endregion } } \ No newline at end of file diff --git a/src/Base/Imp/Blazor/Fusee.Base.Imp.Blazor.csproj b/src/Base/Imp/Blazor/Fusee.Base.Imp.Blazor.csproj index 6747bb2b6f..f273775100 100644 --- a/src/Base/Imp/Blazor/Fusee.Base.Imp.Blazor.csproj +++ b/src/Base/Imp/Blazor/Fusee.Base.Imp.Blazor.csproj @@ -17,10 +17,8 @@ analyzers - - - - + + diff --git a/src/Base/Imp/Desktop/FileDecoder.cs b/src/Base/Imp/Desktop/FileDecoder.cs index 4a7c263322..8dd1740bea 100644 --- a/src/Base/Imp/Desktop/FileDecoder.cs +++ b/src/Base/Imp/Desktop/FileDecoder.cs @@ -1,12 +1,10 @@ using Fusee.Base.Common; using Fusee.Base.Core; - using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using System; using System.IO; -using System.Runtime.InteropServices; using System.Threading.Tasks; namespace Fusee.Base.Imp.Desktop @@ -35,64 +33,67 @@ public static ImageData LoadImage(Stream file) { try { - using var ms = new MemoryStream(); - file.CopyTo(ms); - ms.Position = 0; - - var image = Image.Load(ms, out var imgFormat); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); + //Load the image + using var image = Image.Load(file, out var imgFormat); + //ImageSharp loads from the top-left pixel, whereas OpenGL loads from the bottom-left, causing the texture to be flipped vertically. + //This will correct that, making the texture display properly. + image.Mutate(x => x.Flip(FlipMode.Vertical)); - var bpp = image.PixelType.BitsPerPixel; + var bitsPerPixel = image.PixelType.BitsPerPixel; + var bytesPerPixel = bitsPerPixel / 8; + //Convert ImageSharp's format into a byte array, so we can use it with OpenGL. + var pixels = new byte[bytesPerPixel * image.Width * image.Height]; - switch (image.PixelType.BitsPerPixel) + ImageData img; + switch (bitsPerPixel) { case 16: { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, + (image as Image).CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, new ImagePixelFormat(ColorFormat.Depth16)); + break; } case 24: { var rgb = image as Image; - var bgr = rgb.CloneAs(); - bgr.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, + rgb.CopyPixelDataTo(pixels); + + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, new ImagePixelFormat(ColorFormat.RGB)); + break; } case 32: { var rgba = image as Image; - var bgra = rgba.CloneAs(); - var success = bgra.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); + rgba.CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.RGBA)); + break; } case 48: { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); + (image as Image).CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.fRGB32)); + break; } case 64: { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); + (image as Image).CopyPixelDataTo(pixels); + img = new ImageData(new byte[bytesPerPixel * image.Width * image.Height], image.Width, image.Height, + new ImagePixelFormat(ColorFormat.fRGBA32)); + break; } default: - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); + throw new ArgumentException($"{bitsPerPixel} Bits per pixel not supported!"); } + + Array.Copy(pixels, img.PixelData, pixels.Length); + return img; } catch (Exception ex) { diff --git a/src/Base/Imp/Desktop/FontImp.cs b/src/Base/Imp/Desktop/FontImp.cs index be8f197c79..f8c785bce0 100644 --- a/src/Base/Imp/Desktop/FontImp.cs +++ b/src/Base/Imp/Desktop/FontImp.cs @@ -8,7 +8,7 @@ namespace Fusee.Base.Imp.Desktop /// /// Font implementation using SixLabors.Fonts /// - public class FontImp : FontCore + public class FontImp : FontImpBase { /// /// Font ctor implementation for Desktop @@ -17,7 +17,7 @@ public class FontImp : FontCore public FontImp(Stream stream) { _collection = new FontCollection(); - _collection.Install(stream); + _collection.Add(stream); _font = _collection.Families.AsEnumerable().First().CreateFont(PixelHeight); } } diff --git a/src/Base/Imp/Desktop/Fusee.Base.Imp.Desktop.csproj b/src/Base/Imp/Desktop/Fusee.Base.Imp.Desktop.csproj index 878f38ac88..953fe87b3d 100644 --- a/src/Base/Imp/Desktop/Fusee.Base.Imp.Desktop.csproj +++ b/src/Base/Imp/Desktop/Fusee.Base.Imp.Desktop.csproj @@ -8,6 +8,10 @@ true Fusee Base Imp Desktop + + + + diff --git a/src/Engine/Common/ITexture.cs b/src/Engine/Common/ITexture.cs index 32fadb1ef7..be27fffe60 100644 --- a/src/Engine/Common/ITexture.cs +++ b/src/Engine/Common/ITexture.cs @@ -3,15 +3,13 @@ namespace Fusee.Engine.Common { /// - /// Cross platform abstraction for Textures. - /// Implements and and offers access to internal for GPU upload. + /// Cross platform abstraction for textures, that are used on the gpu. /// - public interface ITexture : IImageData, ITextureBase + public interface ITexture : ITextureBase { /// - /// The byte buffer that makes up the instance. + /// The that makes up the instance. /// - byte[] PixelData { get; } - + IImageData ImageData { get; } } } \ No newline at end of file diff --git a/src/Engine/Common/ITextureBase.cs b/src/Engine/Common/ITextureBase.cs index 954d35073b..e2775473ea 100644 --- a/src/Engine/Common/ITextureBase.cs +++ b/src/Engine/Common/ITextureBase.cs @@ -1,5 +1,4 @@ -using Fusee.Base.Common; -using System; +using System; namespace Fusee.Engine.Common { @@ -116,7 +115,7 @@ public enum TextureFilterMode /// Collection of members all texture types need to implement. /// All textures must be IDisposable. /// - public interface ITextureBase : IImageBase, IDisposable + public interface ITextureBase : IDisposable { /// /// TextureChanged event notifies observing TextureManager about property changes and the Texture's disposal. diff --git a/src/Engine/Common/IVideoStreamImp.cs b/src/Engine/Common/IVideoStreamImp.cs index df217231a0..9058070e3f 100644 --- a/src/Engine/Common/IVideoStreamImp.cs +++ b/src/Engine/Common/IVideoStreamImp.cs @@ -1,4 +1,6 @@ -namespace Fusee.Engine.Common +using Fusee.Base.Common; + +namespace Fusee.Engine.Common { /// /// TODO: Write the actual VideoStream implementations. @@ -11,7 +13,8 @@ public interface IVideoStreamImp /// Gets the current frame. /// /// - ITexture GetCurrentFrame(); + IImageData GetCurrentFrame(); + /// /// Starts this stream. /// diff --git a/src/Engine/Common/IWritableTexture.cs b/src/Engine/Common/IWritableTexture.cs index fae99b2c63..44bd317bc7 100644 --- a/src/Engine/Common/IWritableTexture.cs +++ b/src/Engine/Common/IWritableTexture.cs @@ -1,4 +1,6 @@ -namespace Fusee.Engine.Common +using Fusee.Base.Common; + +namespace Fusee.Engine.Common { /// /// Cross platform abstraction for a WritableTexture. @@ -7,6 +9,21 @@ /// public interface IWritableTexture : ITextureBase { + /// + /// Width in pixels. + /// + int Width { get; } + + /// + /// Height in pixels. + /// + int Height { get; } + + /// + /// Offers additional Information about the color format of the texture. + /// + ImagePixelFormat PixelFormat { get; } + /// /// Type of the render texture, . /// diff --git a/src/Engine/Core/FontMap.cs b/src/Engine/Core/FontMap.cs index d94a1b6266..e60ff8b051 100644 --- a/src/Engine/Core/FontMap.cs +++ b/src/Engine/Core/FontMap.cs @@ -35,14 +35,24 @@ public struct GlyphOnMap public float BitmapT; /// - /// The x-offset of this char on the font map texture. + /// The x-offset of this char on the font map texture in range [0, 1]. /// - public float TexOffX; + public float PosXOnTexPercent; /// - /// The y-offset of this char on the font map texture. + /// The y-offset of this char on the font map texture in range [0, 1]. /// - public float TexOffY; + public float PosYOnTexPercent; + + /// + /// The x-offset of this char on the font map texture in px. + /// + public float PosXOnTex; + + /// + /// The y-offset of this char on the font map texture in px. + /// + public float PosYOnTex; }; /// @@ -59,10 +69,10 @@ public class FontMap /// /// true if up-to-date; otherwise, false. /// - public bool Uptodate { get; private set; } + public bool UpToDate { get; private set; } private Font _font; - private Texture _image; + private ImageData _image; private uint _pixelHeight; private string _alphabet; private readonly Dictionary _glyphOnMapCache; @@ -88,7 +98,7 @@ public FontMap(Font font, uint pixelHeight, string alphabet = null) private void Invalidate() { _glyphOnMapCache.Clear(); - Uptodate = false; + UpToDate = false; } /// @@ -100,34 +110,37 @@ private void Invalidate() /// /// The font image. /// - public Texture Image + public ImageData Image { get { - if (Uptodate) + if (UpToDate) return _image; _font.PixelHeight = _pixelHeight; const int maxWidth = 4096; - var averageAdvance = 0f; + var averageAdvanceX = 0f; + var averageAdvanceY = 0f; var charCount = 0f; //Calculate averageAdvance in Font? Ratio FontSize/ averageAdvance does not change with fontSize foreach (char c in _alphabet) { GlyphInfo gi = _font.GetGlyphInfo(c); - averageAdvance += gi.AdvanceX + 1; + averageAdvanceX += gi.AdvanceX + 1; + averageAdvanceY += gi.AdvanceY + 1; charCount++; } - averageAdvance /= charCount; + averageAdvanceX /= charCount; + averageAdvanceY /= charCount; //_alphabet.ToCharArray().Length * averageAdvance * _pixelHeight is the area of ​​the rectangle with the width equal to the number of letters * averageAdvance and the height equals pixelHeight. // Since this rectangle has the same area as the desired square (texture atlas), the square root of the rectangle is the width of that square. - var widthOld = System.Math.Sqrt(_alphabet.ToCharArray().Length * averageAdvance * _pixelHeight); - var width = (int)System.Math.Pow(2, (int)System.Math.Ceiling(System.Math.Log(widthOld, 2))); + var width = (int)System.Math.Ceiling(System.Math.Sqrt(_alphabet.ToCharArray().Length * averageAdvanceX * averageAdvanceY)); + width = width - (width % 4) + 4; if (width > maxWidth) { @@ -136,7 +149,7 @@ public Texture Image } // Create the font atlas (the texture containing ALL glyphs) - _image = new Texture(new byte[width * width], width, width, new ImagePixelFormat(ColorFormat.Intensity), false); + _image = new ImageData(new byte[width * width], width, width, new ImagePixelFormat(ColorFormat.Intensity)); var offX = 0; var offY = 0; @@ -145,7 +158,8 @@ public Texture Image // Copy each character in the alphabet to the font atlas foreach (char c in _alphabet) { - IImageData glyphImg = _font.RenderGlyph(c, out int bitmapLeft, out int bitmapTop); + var gi = _font.GetGlyphInfo(c); + IImageData glyphImg = _font.GetImageDataForGlyph(c, in gi); if (offX + glyphImg.Width + 1 >= width) { offY += rowH; @@ -163,10 +177,10 @@ public Texture Image { BitmapW = glyphImg.Width, BitmapH = glyphImg.Height, - BitmapL = bitmapLeft, - BitmapT = bitmapTop, - TexOffX = offX / (float)width, - TexOffY = offY / (float)width, + PosXOnTexPercent = offX / (float)width, + PosYOnTexPercent = offY / (float)width, + PosXOnTex = offX, + PosYOnTex = offY }; _glyphOnMapCache[c] = glyphOnMap; @@ -175,7 +189,7 @@ public Texture Image offX += glyphImg.Width + 1; } - Uptodate = true; + UpToDate = true; return _image; } diff --git a/src/Engine/Core/RenderCanvas.cs b/src/Engine/Core/RenderCanvas.cs index e4fa9c8cb2..2852909200 100644 --- a/src/Engine/Core/RenderCanvas.cs +++ b/src/Engine/Core/RenderCanvas.cs @@ -16,8 +16,6 @@ public class RenderCanvas { #region Implementor Fields - public ModuleExtensionPoint PointCloudImplementor { get; set; } - /// /// Gets and sets the canvas implementor. /// diff --git a/src/Engine/Core/RenderContext.cs b/src/Engine/Core/RenderContext.cs index 1548e72247..377f1d85c0 100644 --- a/src/Engine/Core/RenderContext.cs +++ b/src/Engine/Core/RenderContext.cs @@ -1750,26 +1750,19 @@ public void DispatchCompute(int kernelIndex, int threadGroupsX, int threadGroups /// public void Render(Mesh mesh, bool doRenderForward = true) { - try - { - var cFx = GetCompiledFxForRenderMethod(doRenderForward); - SetCompiledFx(cFx.GpuHandle); - SetRenderStateSet(_currentEffect.RendererStates); - SetGlobalParamsInCurrentFx(cFx); - UpdateAllActiveFxParams(cFx); + var cFx = GetCompiledFxForRenderMethod(doRenderForward); + SetCompiledFx(cFx.GpuHandle); + SetRenderStateSet(_currentEffect.RendererStates); + SetGlobalParamsInCurrentFx(cFx); + UpdateAllActiveFxParams(cFx); - var meshImp = _meshManager.GetMeshImpFromMesh(mesh); - _rci.Render(meshImp); + var meshImp = _meshManager.GetMeshImpFromMesh(mesh); + _rci.Render(meshImp); - // After rendering always cleanup pending meshes, textures and shader effects - _meshManager.Cleanup(); - _textureManager.Cleanup(); - _effectManager.Cleanup(); - } - catch (Exception ex) - { - throw new Exception("Error while rendering pass ", ex); - } + // After rendering always cleanup pending meshes, textures and shader effects + _meshManager.Cleanup(); + _textureManager.Cleanup(); + _effectManager.Cleanup(); } /// @@ -1783,26 +1776,19 @@ public void Render(Mesh mesh, bool doRenderForward = true) /// public void Render(GpuMesh mesh, bool doRenderForward = true) { - try - { - var cFx = GetCompiledFxForRenderMethod(doRenderForward); - SetCompiledFx(cFx.GpuHandle); - SetRenderStateSet(_currentEffect.RendererStates); - SetGlobalParamsInCurrentFx(cFx); - UpdateAllActiveFxParams(cFx); + var cFx = GetCompiledFxForRenderMethod(doRenderForward); + SetCompiledFx(cFx.GpuHandle); + SetRenderStateSet(_currentEffect.RendererStates); + SetGlobalParamsInCurrentFx(cFx); + UpdateAllActiveFxParams(cFx); - var meshImp = _meshManager.GetMeshImpFromMesh(mesh); - _rci.Render(meshImp); + var meshImp = _meshManager.GetMeshImpFromMesh(mesh); + _rci.Render(meshImp); - // After rendering always cleanup pending meshes, textures and shader effects - _meshManager.Cleanup(); - _textureManager.Cleanup(); - _effectManager.Cleanup(); - } - catch (Exception ex) - { - throw new Exception("Error while rendering pass ", ex); - } + // After rendering always cleanup pending meshes, textures and shader effects + _meshManager.Cleanup(); + _textureManager.Cleanup(); + _effectManager.Cleanup(); } private float2 CalculateClippingPlanesFromProjection() diff --git a/src/Engine/Core/Texture.cs b/src/Engine/Core/Texture.cs index d24bf6f1e8..4f40d5f38c 100644 --- a/src/Engine/Core/Texture.cs +++ b/src/Engine/Core/Texture.cs @@ -24,7 +24,7 @@ public class Texture : ITexture public Suid SessionUniqueIdentifier { get; private set; } #endregion - private readonly ImageData _imageData; + public IImageData ImageData { get; private set; } #region Properties @@ -38,7 +38,7 @@ public class Texture : ITexture /// public int Width { - get { return _imageData.Width; } + get { return ImageData.Width; } } /// @@ -46,7 +46,7 @@ public int Width /// public int Height { - get { return _imageData.Height; } + get { return ImageData.Height; } } /// @@ -54,7 +54,7 @@ public int Height /// public byte[] PixelData { - get { return _imageData.PixelData; } + get { return ImageData.PixelData; } } /// @@ -62,7 +62,7 @@ public byte[] PixelData /// public ImagePixelFormat PixelFormat { - get { return _imageData.PixelFormat; } + get { return ImageData.PixelFormat; } } /// @@ -136,7 +136,7 @@ public TextureFilterMode FilterMode public Texture(byte[] pixelData, int width, int height, ImagePixelFormat colorFormat, bool generateMipMaps = true, TextureFilterMode filterMode = TextureFilterMode.LinearMipmapLinear, TextureWrapMode wrapMode = TextureWrapMode.Repeat) { SessionUniqueIdentifier = Suid.GenerateSuid(); - _imageData = new ImageData(pixelData, width, height, colorFormat); + ImageData = new ImageData(pixelData, width, height, colorFormat); DoGenerateMipMaps = generateMipMaps; FilterMode = filterMode; WrapMode = wrapMode; @@ -152,10 +152,8 @@ public Texture(byte[] pixelData, int width, int height, ImagePixelFormat colorFo public Texture(IImageData imageData, bool generateMipMaps = true, TextureFilterMode filterMode = TextureFilterMode.NearestMipmapLinear, TextureWrapMode wrapMode = TextureWrapMode.Repeat) { SessionUniqueIdentifier = Suid.GenerateSuid(); - _imageData = new ImageData( - new byte[imageData.Width * imageData.Height * imageData.PixelFormat.BytesPerPixel], - imageData.Width, imageData.Height, imageData.PixelFormat); - _imageData.Blt(0, 0, imageData); + ImageData = imageData; + DoGenerateMipMaps = generateMipMaps; FilterMode = filterMode; WrapMode = wrapMode; @@ -178,7 +176,7 @@ public Texture(IImageData imageData, bool generateMipMaps = true, TextureFilterM public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, int width = 0, int height = 0) { // Blit into private _imageData - _imageData.Blt(xDst, yDst, src, xSrc, ySrc, width, height); + ImageData.Blt(xDst, yDst, src, xSrc, ySrc, width, height); if (width == 0) width = src.Width; if (height == 0) @@ -205,7 +203,7 @@ public void Blt(int xDst, int yDst, IImageData src, int xSrc = 0, int ySrc = 0, /// public IEnumerator ScanLines(int xSrc = 0, int ySrc = 0, int width = 0, int height = 0) { - return _imageData.ScanLines(xSrc, ySrc, width, height); + return ImageData.ScanLines(xSrc, ySrc, width, height); } private bool _disposed; diff --git a/src/Engine/GUI/GuiNode.cs b/src/Engine/GUI/GuiNode.cs index 49d755f72f..4b3308c0ca 100644 --- a/src/Engine/GUI/GuiNode.cs +++ b/src/Engine/GUI/GuiNode.cs @@ -496,7 +496,7 @@ public static async Task CreateAsync(string text, string name, MinMaxR new FxParamDeclaration { Name = UniformNameDeclarations.AlbedoTexture, - Value = new Texture(fontMap.Image) + Value = new Texture(fontMap.Image, false, TextureFilterMode.Nearest) }, new FxParamDeclaration { diff --git a/src/Engine/GUI/GuiText.cs b/src/Engine/GUI/GuiText.cs index 5baf429315..3d8cbc6404 100644 --- a/src/Engine/GUI/GuiText.cs +++ b/src/Engine/GUI/GuiText.cs @@ -203,13 +203,12 @@ private void CreateTextMesh() var glyphOnMap = _fontMap.GetGlyphOnMap(_text[j]); var glyphInfo = _fontMap.Font.GetGlyphInfo(_text[j]); - var x = advanceX + glyphOnMap.BitmapL; - var y = advanceY - glyphOnMap.BitmapT; + var x = advanceX; + var y = advanceY; var w = glyphOnMap.BitmapW; var h = glyphOnMap.BitmapH; advanceX += glyphInfo.AdvanceX; - advanceY += glyphInfo.AdvanceY; // skip glyphs that have no pixels if ((w <= M.EpsilonFloat) || (h <= M.EpsilonFloat)) @@ -217,8 +216,8 @@ private void CreateTextMesh() var bitmapW = glyphOnMap.BitmapW; var bitmapH = glyphOnMap.BitmapH; - var texOffsetX = glyphOnMap.TexOffX; - var texOffsetY = glyphOnMap.TexOffY; + var texOffsetX = glyphOnMap.PosXOnTexPercent; + var texOffsetY = glyphOnMap.PosYOnTexPercent; //account for line height y += lineCnt * lineBreakOffset; diff --git a/src/Engine/Imp/Graphics/Android/RenderContextImp.cs b/src/Engine/Imp/Graphics/Android/RenderContextImp.cs index 588e870fae..fbf790a37d 100644 --- a/src/Engine/Imp/Graphics/Android/RenderContextImp.cs +++ b/src/Engine/Imp/Graphics/Android/RenderContextImp.cs @@ -173,9 +173,9 @@ private DepthFunction GetDepthCompareFunc(Compare compareFunc) }; } - private TextureComponentCount GetTexTureComponentCount(ITextureBase tex) + private TextureComponentCount GetTexTureComponentCount(ImagePixelFormat format) { - return tex.PixelFormat.ColorFormat switch + return format.ColorFormat switch { ColorFormat.RGBA => TextureComponentCount.Rgba, ColorFormat.RGB => TextureComponentCount.Rgb, @@ -195,13 +195,18 @@ private TextureComponentCount GetTexTureComponentCount(ITextureBase tex) After some research it seems the OpenTK 30es branch suffers due to the development of OpenTK 40es.... Furthermore it doesn't seem possible to attach a depth texture to a framebuffer (DEPTH_ATTACHMENT), therefore we need to render depth into a COLOR_ATTACHMENT and create a Depth render buffer. This is bound to create a overhead.*/ - private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) + private TexturePixelInfo GetTexturePixelInfo(ImagePixelFormat pixelFormat) { PixelInternalFormat internalFormat; PixelFormat format; PixelType pxType; - switch (tex.PixelFormat.ColorFormat) + //The wrong row alignment will lead to malformed textures. + //See https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_upload_and_pixel_reads + //and https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout + int rowAlignment = 4; + + switch (pixelFormat.ColorFormat) { case ColorFormat.RGBA: internalFormat = PixelInternalFormat.Rgba; @@ -213,12 +218,14 @@ private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) internalFormat = PixelInternalFormat.Rgb; format = PixelFormat.Rgb; pxType = PixelType.UnsignedByte; + rowAlignment = 1; break; // TODO: Handle Alpha-only / Intensity-only and AlphaIntensity correctly. case ColorFormat.Intensity: internalFormat = PixelInternalFormat.Alpha; format = PixelFormat.Alpha; pxType = PixelType.UnsignedByte; + rowAlignment = 1; break; case ColorFormat.Depth24: @@ -236,6 +243,7 @@ private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) internalFormat = PixelInternalFormat.Rgb; format = PixelFormat.Rgb; pxType = PixelType.UnsignedByte; + rowAlignment = 1; break; case ColorFormat.fRGB32: @@ -293,7 +301,8 @@ private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) { Format = format, InternalFormat = internalFormat, - PxType = pxType + PxType = pxType, + RowAlignment = rowAlignment }; } @@ -311,9 +320,9 @@ public ITextureHandle CreateTexture(IWritableArrayTexture img) var minFilter = glMinMagFilter.Item1; var magFilter = glMinMagFilter.Item2; var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.PixelFormat); - GL.TexImage3D(TextureTarget3D.Texture2DArray, 0, GetTexTureComponentCount(img), img.Width, img.Height, img.Layers, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); + GL.TexImage3D(TextureTarget3D.Texture2DArray, 0, GetTexTureComponentCount(img.PixelFormat), img.Width, img.Height, img.Layers, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); if (img.DoGenerateMipMaps) GL.GenerateMipmap(TextureTarget.Texture2DArray); @@ -346,7 +355,7 @@ public ITextureHandle CreateTexture(IWritableCubeMap img) var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.PixelFormat); for (int i = 0; i < 6; i++) GL.TexImage2D(TextureTarget.TextureCubeMapPositiveX + i, 0, pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); @@ -378,9 +387,10 @@ public ITextureHandle CreateTexture(ITexture img) var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.ImageData.PixelFormat); - GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, img.PixelData); + GL.PixelStore(PixelStoreParameter.UnpackAlignment, pxInfo.RowAlignment); + GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, img.ImageData.Width, img.ImageData.Height, 0, pxInfo.Format, pxInfo.PxType, img.ImageData.PixelData); if (img.DoGenerateMipMaps) GL.GenerateMipmap(TextureTarget.Texture2D); @@ -414,7 +424,7 @@ public ITextureHandle CreateTexture(IWritableTexture img) var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.PixelFormat); GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); @@ -449,11 +459,11 @@ public ITextureHandle CreateTexture(IWritableTexture img) /// /// Look at the VideoTextureExample for further information. public void UpdateTextureRegion(ITextureHandle tex, ITexture img, int startX, int startY, int width, int height) { - PixelFormat format = GetTexturePixelInfo(img).Format; + PixelFormat format = GetTexturePixelInfo(img.ImageData.PixelFormat).Format; GL.BindTexture(TextureTarget.Texture2D, ((TextureHandle)tex).TexHandle); GL.TexSubImage2D(TextureTarget.Texture2D, 0, startX, startY, width, height, - format, PixelType.UnsignedByte, img.PixelData); + format, PixelType.UnsignedByte, img.ImageData.PixelData); } /// diff --git a/src/Engine/Imp/Graphics/Android/TexturePixelInfo.cs b/src/Engine/Imp/Graphics/Android/TexturePixelInfo.cs index 1e8159013a..169befdf88 100644 --- a/src/Engine/Imp/Graphics/Android/TexturePixelInfo.cs +++ b/src/Engine/Imp/Graphics/Android/TexturePixelInfo.cs @@ -9,5 +9,6 @@ internal struct TexturePixelInfo : ITexturePixelInfo public PixelInternalFormat InternalFormat; public PixelFormat Format; public PixelType PxType; + public int RowAlignment; } } \ No newline at end of file diff --git a/src/Engine/Imp/Graphics/Blazor/RenderContextImp.cs b/src/Engine/Imp/Graphics/Blazor/RenderContextImp.cs index 7483600cc7..de9108e004 100644 --- a/src/Engine/Imp/Graphics/Blazor/RenderContextImp.cs +++ b/src/Engine/Imp/Graphics/Blazor/RenderContextImp.cs @@ -130,13 +130,18 @@ private uint GetDepthCompareFunc(Compare compareFunc) }; } - private static TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) + private static TexturePixelInfo GetTexturePixelInfo(ImagePixelFormat pxFormat) { uint internalFormat; uint format; uint pxType; - switch (tex.PixelFormat.ColorFormat) + //The wrong row alignment will lead to malformed textures. + //See https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_upload_and_pixel_reads + //and https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout + int rowAlignment = 4; + + switch (pxFormat.ColorFormat) { case ColorFormat.RGBA: internalFormat = RGBA; @@ -147,11 +152,13 @@ private static TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) internalFormat = RGB; format = RGB; pxType = UNSIGNED_BYTE; + rowAlignment = 1; break; case ColorFormat.Intensity: internalFormat = R8; format = RED; pxType = UNSIGNED_BYTE; + rowAlignment = 1; break; case ColorFormat.Depth24: internalFormat = DEPTH_COMPONENT24; @@ -167,6 +174,7 @@ private static TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) internalFormat = RGBA8UI; format = RGBA; pxType = UNSIGNED_INT; + rowAlignment = 1; break; case ColorFormat.fRGB32: throw new NotSupportedException("WebGL 2.0: fRGB32 not supported"); @@ -189,19 +197,20 @@ private static TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) pxType = FLOAT; break; default: - throw new ArgumentOutOfRangeException($"CreateTexture: Image pixel format not supported {tex.PixelFormat.ColorFormat}"); + throw new ArgumentOutOfRangeException($"CreateTexture: Image pixel format not supported {pxFormat.ColorFormat}"); } return new TexturePixelInfo() { Format = format, InternalFormat = internalFormat, - PxType = pxType + PxType = pxType, + RowAlignment = rowAlignment }; } - private static Array GetEmptyArray(ITextureBase tex) + private static Array GetEmptyArray(IWritableTexture tex) { return tex.PixelFormat.ColorFormat switch { @@ -233,7 +242,7 @@ public ITextureHandle CreateTexture(IWritableCubeMap img) int magFilter = glMinMagFilter.Item2; int glWrapMode = GetWrapMode(img.WrapMode); - TexturePixelInfo pxInfo = GetTexturePixelInfo(img); + TexturePixelInfo pxInfo = GetTexturePixelInfo(img.PixelFormat); var data = GetEmptyArray(img); @@ -274,11 +283,12 @@ public ITextureHandle CreateTexture(ITexture img) var maxFilter = glMinMagFilter.Item2; int glWrapMode = GetWrapMode(img.WrapMode); - TexturePixelInfo pxInfo = GetTexturePixelInfo(img); + TexturePixelInfo pxInfo = GetTexturePixelInfo(img.ImageData.PixelFormat); - gl2.TexImage2D(TEXTURE_2D, 0, (int)pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, img.PixelData); + gl2.PixelStorei(UNPACK_ALIGNMENT, pxInfo.RowAlignment); + gl2.TexImage2D(TEXTURE_2D, 0, (int)pxInfo.InternalFormat, img.ImageData.Width, img.ImageData.Height, 0, pxInfo.Format, pxInfo.PxType, img.ImageData.PixelData); - if (img.DoGenerateMipMaps && img.PixelFormat.ColorFormat != ColorFormat.Intensity) + if (img.DoGenerateMipMaps && img.ImageData.PixelFormat.ColorFormat != ColorFormat.Intensity) gl2.GenerateMipmap(TEXTURE_2D); gl2.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, minFilter); @@ -289,7 +299,7 @@ public ITextureHandle CreateTexture(ITexture img) uint err = gl2.GetError(); if (err != 0) - throw new ArgumentException($"Create Texture ITexture gl2 error {err}, Format {img.PixelFormat.ColorFormat}, BPP {img.PixelFormat.BytesPerPixel}, {pxInfo.InternalFormat}"); + throw new ArgumentException($"Create Texture ITexture gl2 error {err}, Format {img.ImageData.PixelFormat.ColorFormat}, BPP {img.ImageData.PixelFormat.BytesPerPixel}, {pxInfo.InternalFormat}"); ITextureHandle texID = new TextureHandle { TexHandle = id }; @@ -311,7 +321,7 @@ public ITextureHandle CreateTexture(IWritableTexture img) var magFilter = glMinMagFilter.Item2; int glWrapMode = GetWrapMode(img.WrapMode); - TexturePixelInfo pxInfo = GetTexturePixelInfo(img); + TexturePixelInfo pxInfo = GetTexturePixelInfo(img.PixelFormat); Array imgData = GetEmptyArray(img); @@ -350,13 +360,13 @@ public ITextureHandle CreateTexture(IWritableTexture img) /// /// Look at the VideoTextureExample for further information. public void UpdateTextureRegion(ITextureHandle tex, ITexture img, int startX, int startY, int width, int height) { - TexturePixelInfo info = GetTexturePixelInfo(img); + TexturePixelInfo info = GetTexturePixelInfo(img.ImageData.PixelFormat); uint format = info.Format; uint pxType = info.PxType; // copy the bytes from img to GPU texture - int bytesTotal = width * height * img.PixelFormat.BytesPerPixel; - IEnumerator scanlines = img.ScanLines(startX, startY, width, height); + int bytesTotal = width * height * img.ImageData.PixelFormat.BytesPerPixel; + IEnumerator scanlines = img.ImageData.ScanLines(startX, startY, width, height); byte[] bytes = new byte[bytesTotal]; int offset = 0; do @@ -2374,7 +2384,7 @@ public ITextureHandle CreateTexture(IWritableArrayTexture img) var minFilter = glMinMagFilter.Item1; var magFilter = glMinMagFilter.Item2; var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.PixelFormat); var data = new uint[img.Width * img.Height * img.Layers]; diff --git a/src/Engine/Imp/Graphics/Blazor/TexturePixelInfo.cs b/src/Engine/Imp/Graphics/Blazor/TexturePixelInfo.cs index c97c6c631e..c5c68b79a5 100644 --- a/src/Engine/Imp/Graphics/Blazor/TexturePixelInfo.cs +++ b/src/Engine/Imp/Graphics/Blazor/TexturePixelInfo.cs @@ -10,5 +10,6 @@ internal struct TexturePixelInfo : ITexturePixelInfo public uint InternalFormat; public uint Format; public uint PxType; + public int RowAlignment; } } \ No newline at end of file diff --git a/src/Engine/Imp/Graphics/Desktop/RenderCanvasImp.cs b/src/Engine/Imp/Graphics/Desktop/RenderCanvasImp.cs index 7be35b611f..bdbb4736ba 100644 --- a/src/Engine/Imp/Graphics/Desktop/RenderCanvasImp.cs +++ b/src/Engine/Imp/Graphics/Desktop/RenderCanvasImp.cs @@ -4,7 +4,6 @@ using OpenTK.Windowing.Common.Input; using OpenTK.Windowing.Desktop; using OpenTK.Windowing.GraphicsLibraryFramework; -using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using System; @@ -244,16 +243,14 @@ public RenderCanvasImp(ImageData icon = null, bool isMultithreaded = false) if (icon != null) { // convert Bgra to Rgba for OpenTK.WindowIcon - var pxData = SixLabors.ImageSharp.Image.LoadPixelData(icon.PixelData, icon.Width, icon.Height); - var bgra = pxData.CloneAs(); - bgra.Mutate(x => x.AutoOrient()); - bgra.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - if (!bgra.TryGetSinglePixelSpan(out var res)) - { - Diagnostics.Warn("Couldn't convert icon image to Rgba32!"); - return; - } + var res = new Span(new Rgba32[width * height]); + var pxData = SixLabors.ImageSharp.Image.LoadPixelData(icon.PixelData, icon.Width, icon.Height); + pxData.Mutate(x => x.AutoOrient()); + pxData.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); + + pxData.CopyPixelDataTo(res); + var resBytes = MemoryMarshal.AsBytes(res.ToArray()); _gameWindow.Icon = new WindowIcon(new Image[] { new Image(icon.Width, icon.Height, resBytes.ToArray()) }); } @@ -443,15 +440,16 @@ public SixLabors.ImageSharp.Image ShootCurrentFrame(int width, int height) DoRender(); DoResize(width, height); - var bmp = new Image(Width, Height); - bmp.TryGetSinglePixelSpan(out var mem); + var mem = new byte[width * height * 4]; GL.PixelStore(PixelStoreParameter.PackRowLength, 1); - GL.ReadPixels(0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, ref mem[0]); + GL.ReadPixels(0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, mem); + + var img = SixLabors.ImageSharp.Image.LoadPixelData(mem, Width, Height); - bmp.Mutate(x => x.AutoOrient()); - bmp.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); + img.Mutate(x => x.AutoOrient()); + img.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - return bmp; + return img; } #endregion diff --git a/src/Engine/Imp/Graphics/Desktop/RenderContextImp.cs b/src/Engine/Imp/Graphics/Desktop/RenderContextImp.cs index b008c40d42..2d4e95a5cf 100644 --- a/src/Engine/Imp/Graphics/Desktop/RenderContextImp.cs +++ b/src/Engine/Imp/Graphics/Desktop/RenderContextImp.cs @@ -7,7 +7,6 @@ using OpenTK.Graphics.OpenGL; using System; using System.Collections.Generic; -using System.Drawing; using System.Linq; namespace Fusee.Engine.Imp.Graphics.Desktop @@ -190,50 +189,59 @@ private SizedInternalFormat GetSizedInteralFormat(ImagePixelFormat format) }; } - private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) + private TexturePixelInfo GetTexturePixelInfo(ImagePixelFormat pixelFormat) { PixelInternalFormat internalFormat; PixelFormat format; PixelType pxType; - switch (tex.PixelFormat.ColorFormat) + //The wrong row alignment will lead to malformed textures. + //See https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_upload_and_pixel_reads + //and https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout + int rowAlignment = 4; + + switch (pixelFormat.ColorFormat) { case ColorFormat.RGBA: internalFormat = PixelInternalFormat.Rgba; - format = PixelFormat.Bgra; + format = PixelFormat.Rgba; pxType = PixelType.UnsignedByte; - break; + case ColorFormat.RGB: internalFormat = PixelInternalFormat.Rgb; - format = PixelFormat.Bgr; + format = PixelFormat.Rgb; pxType = PixelType.UnsignedByte; - + rowAlignment = 1; break; + // TODO: Handle Alpha-only / Intensity-only and AlphaIntensity correctly. case ColorFormat.Intensity: internalFormat = PixelInternalFormat.R8; format = PixelFormat.Red; pxType = PixelType.UnsignedByte; - + rowAlignment = 1; break; + case ColorFormat.Depth24: internalFormat = PixelInternalFormat.DepthComponent24; format = PixelFormat.DepthComponent; pxType = PixelType.Float; - break; + case ColorFormat.Depth16: internalFormat = PixelInternalFormat.DepthComponent16; format = PixelFormat.DepthComponent; pxType = PixelType.Float; break; + case ColorFormat.uiRgb8: internalFormat = PixelInternalFormat.Rgba8ui; format = PixelFormat.RgbaInteger; pxType = PixelType.UnsignedByte; - + rowAlignment = 1; break; + case ColorFormat.fRGB32: internalFormat = PixelInternalFormat.Rgb32f; format = PixelFormat.Rgb; @@ -245,11 +253,13 @@ private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) format = PixelFormat.Rgb; pxType = PixelType.Float; break; + case ColorFormat.fRGBA16: internalFormat = PixelInternalFormat.Rgba16f; format = PixelFormat.Rgba; pxType = PixelType.Float; break; + case ColorFormat.fRGBA32: internalFormat = PixelInternalFormat.Rgba32f; format = PixelFormat.Rgba; @@ -270,7 +280,8 @@ private TexturePixelInfo GetTexturePixelInfo(ITextureBase tex) { Format = format, InternalFormat = internalFormat, - PxType = pxType + PxType = pxType, + RowAlignment = rowAlignment }; } @@ -288,7 +299,7 @@ public ITextureHandle CreateTexture(IWritableArrayTexture img) var minFilter = glMinMagFilter.Item1; var magFilter = glMinMagFilter.Item2; var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.PixelFormat); GL.TexImage3D(TextureTarget.Texture2DArray, 0, pxInfo.InternalFormat, img.Width, img.Height, img.Layers, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); @@ -322,7 +333,7 @@ public ITextureHandle CreateTexture(IWritableCubeMap img) var magFilter = glMinMagFilter.Item2; var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.PixelFormat); for (int i = 0; i < 6; i++) GL.TexImage2D(TextureTarget.TextureCubeMapPositiveX + i, 0, pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); @@ -356,9 +367,10 @@ public ITextureHandle CreateTexture(ITexture img) var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var pxInfo = GetTexturePixelInfo(img.ImageData.PixelFormat); - GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, img.PixelData); + GL.PixelStore(PixelStoreParameter.UnpackAlignment, pxInfo.RowAlignment); + GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, img.ImageData.Width, img.ImageData.Height, 0, pxInfo.Format, pxInfo.PxType, img.ImageData.PixelData); if (img.DoGenerateMipMaps) GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); @@ -377,26 +389,26 @@ public ITextureHandle CreateTexture(ITexture img) /// /// Creates a new Texture and binds it to the shader. /// - /// A given IWritableTexture object, containing all necessary information for the upload to the graphics card. + /// A given IWritableTexture object, containing all necessary information for the upload to the graphics card. /// An ITextureHandle that can be used for texturing in the shader. In this implementation, the handle is an integer-value which is necessary for OpenTK. - public ITextureHandle CreateTexture(IWritableTexture img) + public ITextureHandle CreateTexture(IWritableTexture tex) { int id = GL.GenTexture(); GL.BindTexture(TextureTarget.Texture2D, id); - var glMinMagFilter = GetMinMagFilter(img.FilterMode); + var glMinMagFilter = GetMinMagFilter(tex.FilterMode); var minFilter = glMinMagFilter.Item1; var magFilter = glMinMagFilter.Item2; - var glWrapMode = GetWrapMode(img.WrapMode); - var pxInfo = GetTexturePixelInfo(img); + var glWrapMode = GetWrapMode(tex.WrapMode); + var pxInfo = GetTexturePixelInfo(tex.PixelFormat); - GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, img.Width, img.Height, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); + GL.TexImage2D(TextureTarget.Texture2D, 0, pxInfo.InternalFormat, tex.Width, tex.Height, 0, pxInfo.Format, pxInfo.PxType, IntPtr.Zero); - if (img.DoGenerateMipMaps) + if (tex.DoGenerateMipMaps) GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureCompareMode, (int)GetTexComapreMode(img.CompareMode)); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureCompareFunc, (int)GetDepthCompareFunc(img.CompareFunc)); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureCompareMode, (int)GetTexComapreMode(tex.CompareMode)); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureCompareFunc, (int)GetDepthCompareFunc(tex.CompareFunc)); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)minFilter); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)magFilter); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)glWrapMode); @@ -419,11 +431,12 @@ public ITextureHandle CreateTexture(IWritableTexture img) /// /// Look at the VideoTextureExample for further information. public void UpdateTextureRegion(ITextureHandle tex, ITexture img, int startX, int startY, int width, int height) { - PixelFormat format = GetTexturePixelInfo(img).Format; + var pxInfo = GetTexturePixelInfo(img.ImageData.PixelFormat); + PixelFormat format = pxInfo.Format; // copy the bytes from img to GPU texture - int bytesTotal = width * height * img.PixelFormat.BytesPerPixel; - var scanlines = img.ScanLines(startX, startY, width, height); + int bytesTotal = width * height * img.ImageData.PixelFormat.BytesPerPixel; + var scanlines = img.ImageData.ScanLines(startX, startY, width, height); byte[] bytes = new byte[bytesTotal]; int offset = 0; do @@ -437,6 +450,7 @@ public void UpdateTextureRegion(ITextureHandle tex, ITexture img, int startX, in } while (scanlines.MoveNext()); + GL.PixelStore(PixelStoreParameter.PackAlignment, pxInfo.RowAlignment); GL.BindTexture(TextureTarget.Texture2D, ((TextureHandle)tex).TexHandle); GL.TexSubImage2D(TextureTarget.Texture2D, 0, startX, startY, width, height, format, PixelType.UnsignedByte, bytes); @@ -2039,7 +2053,7 @@ public void SetRenderState(RenderState renderState, uint value) } break; case RenderState.BlendFactor: - GL.BlendColor(Color.FromArgb((int)value)); + GL.BlendColor(System.Drawing.Color.FromArgb((int)value)); break; default: throw new ArgumentOutOfRangeException(nameof(renderState)); @@ -2591,7 +2605,7 @@ public void DeleteStorageBuffer(IBufferHandle storageBufferHandle) public IImageData GetPixelColor(int x, int y, int w = 1, int h = 1) { ImageData image = ImageData.CreateImage(w, h, ColorUint.Black); - GL.ReadPixels(x, y, w, h, PixelFormat.Bgr /* yuk, yuk ??? */, PixelType.UnsignedByte, image.PixelData); + GL.ReadPixels(x, y, w, h, PixelFormat.Rgb, PixelType.UnsignedByte, image.PixelData); return image; } diff --git a/src/Engine/Imp/Graphics/Desktop/TexturePixelInfo.cs b/src/Engine/Imp/Graphics/Desktop/TexturePixelInfo.cs index 4ba340cf8f..c9643898ec 100644 --- a/src/Engine/Imp/Graphics/Desktop/TexturePixelInfo.cs +++ b/src/Engine/Imp/Graphics/Desktop/TexturePixelInfo.cs @@ -9,5 +9,6 @@ internal struct TexturePixelInfo : ITexturePixelInfo public PixelInternalFormat InternalFormat; public PixelFormat Format; public PixelType PxType; + public int RowAlignment; } } \ No newline at end of file diff --git a/src/Engine/Player/Blazor/Main.cs b/src/Engine/Player/Blazor/Main.cs index 113b16b4d2..1f7af67755 100644 --- a/src/Engine/Player/Blazor/Main.cs +++ b/src/Engine/Player/Blazor/Main.cs @@ -7,14 +7,10 @@ using Fusee.Serialization; using Microsoft.JSInterop; using ProtoBuf; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using System; using System.Linq; using System.Net.Http; using System.Reflection; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Path = System.IO.Path; using Stream = System.IO.Stream; @@ -86,103 +82,6 @@ public async override void Run() } }); - // Image handler - fap.RegisterTypeHandler(new AssetHandler - { - ReturnedType = typeof(Base.Core.ImageData), - Decoder = (_, __) => throw new NotImplementedException("Non-async decoder isn't supported in Blazor builds"), - DecoderAsync = async (string id, object storage) => - { - var ext = Path.GetExtension(id).ToLower(); - - try - { - //using var ms = new MemoryStream(); - //((Stream)storage).CopyTo(ms); - using var image = await Image.LoadAsync((Stream)storage); - - image.Mutate(x => x.AutoOrient()); - image.Mutate(x => x.RotateFlip(RotateMode.None, FlipMode.Vertical)); - var ret = ReadPixels(image); - - return ret; - - // inner method to prevent Span inside async method error - static ImageData ReadPixels(Image image) - { - var bpp = image.PixelType.BitsPerPixel; - - switch (image.PixelType.BitsPerPixel) - { - case 16: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.Depth16)); - } - case 24: - { - var rgb = image as Image; - - rgb.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGB)); - } - case 32: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.RGBA)); - } - case 48: - { - var rgba = image as Image; - - rgba.TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGB32)); - } - case 64: - { - (image as Image).TryGetSinglePixelSpan(out var res); - var resBytes = MemoryMarshal.AsBytes(res.ToArray()); - return new ImageData(resBytes.ToArray(), image.Width, image.Height, - new ImagePixelFormat(ColorFormat.fRGBA32)); - } - default: - { - Console.WriteLine($"Error converting! {bpp}"); - - throw new ArgumentException($"{bpp} Bits per pixel not supported!"); - - } - } - }; - } - catch (Exception ex) - { - Console.WriteLine($"Error loading/converting image {id} {ex}"); - Diagnostics.Error($"Error loading/converting image {id}", ex); - - // return empty 1x1 image - return new ImageData(new byte[] { 0, 0, 0, 1, 0, 0, 0, 1 }, 1, 1, - new ImagePixelFormat(ColorFormat.RGBA)); - - } - - }, - Checker = (string id) => - { - var ext = Path.GetExtension(id).ToLower(); - return true; - } - }); AssetStorage.RegisterProvider(fap); diff --git a/src/PointCloud/Potree/V2/Potree2Writer.cs b/src/PointCloud/Potree/V2/Potree2Writer.cs index 808b2a7017..50e8f0c583 100644 --- a/src/PointCloud/Potree/V2/Potree2Writer.cs +++ b/src/PointCloud/Potree/V2/Potree2Writer.cs @@ -1,5 +1,4 @@ using Fusee.Math.Core; -using Fusee.PointCloud.Common; using Fusee.PointCloud.Common.Accessors; using Fusee.PointCloud.Potree.V2.Data; using Newtonsoft.Json; diff --git a/src/Tests/Render/Desktop/Fusee.Tests.Render.Desktop.csproj b/src/Tests/Render/Desktop/Fusee.Tests.Render.Desktop.csproj index 1c9bb4bf9f..fb74a08c6a 100644 --- a/src/Tests/Render/Desktop/Fusee.Tests.Render.Desktop.csproj +++ b/src/Tests/Render/Desktop/Fusee.Tests.Render.Desktop.csproj @@ -33,7 +33,7 @@ - + all diff --git a/src/Tests/Render/Desktop/Program.cs b/src/Tests/Render/Desktop/Program.cs index 05d19d6a06..67d100246b 100644 --- a/src/Tests/Render/Desktop/Program.cs +++ b/src/Tests/Render/Desktop/Program.cs @@ -1,9 +1,11 @@ -using Fusee.Base.Common; +using Fusee.Base.Common; using Fusee.Base.Core; using Fusee.Base.Imp.Desktop; using Fusee.Engine.Core; using Fusee.Engine.Core.Scene; using Fusee.Serialization; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using System; using System.IO; @@ -66,9 +68,11 @@ public static void Init(string arg) app.InitApp(); app.Init(); + var renderTex = new WritableTexture(Engine.Common.RenderTargetTextureTypes.Albedo, new ImagePixelFormat(ColorFormat.RGBA), width, height); + app.RC.SetRenderTarget(renderTex); // Render a single frame and save it - var bmp = cimp.ShootCurrentFrame(width, height); - bmp.Save(arg, System.Drawing.Imaging.ImageFormat.Png); + using var img = cimp.ShootCurrentFrame(width, height) as Image; + img.SaveAsPng(arg); // Done Console.Error.WriteLine($"SUCCESS: Image {arg} generated."); diff --git a/src/Tests/Render/Desktop/RenderTests.cs b/src/Tests/Render/Desktop/RenderTests.cs index c9f29c7063..65e4bca9ca 100644 --- a/src/Tests/Render/Desktop/RenderTests.cs +++ b/src/Tests/Render/Desktop/RenderTests.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; @@ -19,10 +20,11 @@ public void AdvancedUITest() Program.Example = new Fusee.Examples.AdvancedUI.Core.AdvancedUI() { rnd = new System.Random(12345) }; Program.Init("AdvancedUITest.png"); - var referenceIm = new Bitmap(@"References\AdvancedUI.png"); - var testIm = new Bitmap("AdvancedUITest.png"); - - var percent = CompareImage(referenceIm, testIm); + var referenceIm = Image.Load(@"References\AdvancedUI.png"); + //var referenceIm = Image.Load(@"References\AdvancedUI.png"); + var testIm = Image.Load("AdvancedUITest.png"); + //var testIm = Image.Load("AdvancedUITest.png"); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.99f, 1f); output.WriteLine(percent.ToString()); @@ -34,10 +36,10 @@ public void AdvancedUITest() // Program.Example = new Fusee.Examples.Bone.Core.Bone(); // Program.Init("BoneAnimationTest.png"); - // var referenceIm = new Bitmap(@"References\BoneAnimation.png"); - // var testIm = new Bitmap("BoneAnimationTest.png"); + // var referenceIm = Image.Load(@"References\BoneAnimation.png"); + // var testIm = Image.Load("BoneAnimationTest.png"); - // var percent = CompareImage(referenceIm, testIm); + // var percent = CompareImage(referenceIm as Image, testIm as Image); // Assert.InRange(percent, 0.99f, 1f); // output.WriteLine(percent.ToString()); @@ -49,10 +51,10 @@ public void GeometryEditingTest() Program.Example = new Fusee.Examples.GeometryEditing.Core.GeometryEditing(); Program.Init("GeometryEditingTest.png"); - var referenceIm = new Bitmap(@"References\GeometryEditing.png"); - var testIm = new Bitmap("GeometryEditingTest.png"); + var referenceIm = Image.Load(@"References\GeometryEditing.png"); + var testIm = Image.Load("GeometryEditingTest.png"); - var percent = CompareImage(referenceIm, testIm); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.99f, 1f); output.WriteLine(percent.ToString()); @@ -64,10 +66,10 @@ public void MeshingAroundTest() Program.Example = new Fusee.Examples.MeshingAround.Core.MeshingAround(); Program.Init("MeshingAroundTest.png"); - var referenceIm = new Bitmap(@"References\MeshingAround.png"); - var testIm = new Bitmap("MeshingAroundTest.png"); + var referenceIm = Image.Load(@"References\MeshingAround.png"); + var testIm = Image.Load("MeshingAroundTest.png"); - var percent = CompareImage(referenceIm, testIm); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.99f, 1f); output.WriteLine(percent.ToString()); @@ -79,10 +81,10 @@ public void PickingTest() Program.Example = new Fusee.Examples.Picking.Core.Picking(); Program.Init("PickingTest.png"); - var referenceIm = new Bitmap(@"References\Picking.png"); - var testIm = new Bitmap("PickingTest.png"); + var referenceIm = Image.Load(@"References\Picking.png"); + var testIm = Image.Load("PickingTest.png"); - var percent = CompareImage(referenceIm, testIm); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.99f, 1f); output.WriteLine(percent.ToString()); @@ -94,10 +96,10 @@ public void SimpleTest() Program.Example = new Fusee.Examples.Simple.Core.Simple(); Program.Init("SimpleTest.png"); - var referenceIm = new Bitmap(@"References\Simple.png"); - var testIm = new Bitmap("SimpleTest.png"); + var referenceIm = Image.Load(@"References\Simple.png"); + var testIm = Image.Load("SimpleTest.png"); - var percent = CompareImage(referenceIm, testIm); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.98f, 1f); output.WriteLine(percent.ToString()); @@ -109,10 +111,10 @@ public void SimpleTest() // Program.Example = new Fusee.Examples.SimpleDeferred.Core.SimpleDeferred(); // Program.Init("SimpleDeferredTest.png"); - // var referenceIm = new Bitmap(@"References\SimpleDeferred.png"); - // var testIm = new Bitmap("SimpleDeferredTest.png"); + // var referenceIm = Image.Load(@"References\SimpleDeferred.png"); + // var testIm = Image.Load("SimpleDeferredTest.png"); - // var percent = CompareImage(referenceIm, testIm); + // var percent = CompareImage(referenceIm as Image, testIm as Image); // Assert.InRange(percent, 0.01f, 1f); // output.WriteLine(percent.ToString()); @@ -124,10 +126,10 @@ public void ThreeDFontTest() Program.Example = new Fusee.Examples.ThreeDFont.Core.ThreeDFont(); Program.Init("ThreeDFontTest.png"); - var referenceIm = new Bitmap(@"References\ThreeDFont.png"); - var testIm = new Bitmap("ThreeDFontTest.png"); + var referenceIm = Image.Load(@"References\ThreeDFont.png"); + var testIm = Image.Load("ThreeDFontTest.png"); - var percent = CompareImage(referenceIm, testIm); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.99f, 1f); output.WriteLine(percent.ToString()); @@ -139,10 +141,10 @@ public void UITest() Program.Example = new Fusee.Examples.UI.Core.UI(); Program.Init("UITest.png"); - var referenceIm = new Bitmap(@"References\UI.png"); - var testIm = new Bitmap("UITest.png"); + var referenceIm = Image.Load(@"References\UI.png"); + var testIm = Image.Load("UITest.png"); - var percent = CompareImage(referenceIm, testIm); + var percent = CompareImage(referenceIm as Image, testIm as Image); Assert.InRange(percent, 0.99f, 1f); output.WriteLine(percent.ToString()); @@ -154,7 +156,7 @@ public void UITest() /// The reference image to compare to. /// The image that is to be compared. /// The percentage of pixels not the same in the two images. - private static float CompareImage(Bitmap referenceIm, Bitmap testIm) + private static float CompareImage(Image referenceIm, Image testIm) { var count = 0; @@ -162,7 +164,7 @@ private static float CompareImage(Bitmap referenceIm, Bitmap testIm) { for (int y = 0; y < System.Math.Min(referenceIm.Height, testIm.Height); y++) { - if (!testIm.GetPixel(x, y).Equals(referenceIm.GetPixel(x, y))) + if (!testIm[x, y].Equals(referenceIm[x, y])) count++; } }