diff --git a/.editorconfig b/.editorconfig index fa43757a9..b725c5cce 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,4 +13,8 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:warning dotnet_style_predefined_type_for_member_access = true:warning dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_method = true:warning -dotnet_style_qualification_for_property = true:warning \ No newline at end of file +dotnet_style_qualification_for_property = true:warning + +[*.tt] +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index fb8af2320..8034c8c89 100644 --- a/.gitignore +++ b/.gitignore @@ -217,3 +217,4 @@ artifacts/ #CodeCoverage **/CodeCoverage/* docs/ +/samples/AvatarWithRoundedCorner/output diff --git a/ImageSharp.sln b/ImageSharp.sln index 2e0cbd52e..35998e1aa 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject @@ -45,6 +45,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{96188137-5FA6-4924-AB6E-4EFF79C6E0BB}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -127,6 +131,18 @@ Global {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x64.Build.0 = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.ActiveCfg = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x86.Build.0 = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|Any CPU.Build.0 = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.ActiveCfg = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x64.Build.0 = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.ActiveCfg = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Debug|x86.Build.0 = Debug|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.ActiveCfg = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|Any CPU.Build.0 = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.ActiveCfg = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU + {844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -139,5 +155,6 @@ Global {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} EndGlobalSection EndGlobal diff --git a/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj b/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj new file mode 100644 index 000000000..e000aacf1 --- /dev/null +++ b/samples/AvatarWithRoundedCorner/AvatarWithRoundedCorner.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp1.1 + + + + + + + \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs new file mode 100644 index 000000000..5516a73fb --- /dev/null +++ b/samples/AvatarWithRoundedCorner/Program.cs @@ -0,0 +1,70 @@ + + +namespace AvatarWithRoundedCorner +{ + using System; + using System.Numerics; + using ImageSharp; + using SixLabors.Shapes; + + class Program + { + static void Main(string[] args) + { + System.IO.Directory.CreateDirectory("output"); + + GenerateAvatar("fb.jpg", "output/fb.png", new ImageSharp.Size(200, 200), 20); + GenerateAvatar("fb.jpg", "output/fb-round.png", new ImageSharp.Size(200, 200), 100); + GenerateAvatar("fb.jpg", "output/fb-rounder.png", new ImageSharp.Size(200, 200), 150); + } + + private static void GenerateAvatar(string source, string destination, ImageSharp.Size size, float cornerRadius) + { + using (var image = Image.Load(source)) + { + image.Resize(new ImageSharp.Processing.ResizeOptions + { + Size = size, + Mode = ImageSharp.Processing.ResizeMode.Crop + }); + + ApplyRoundedCourners(image, cornerRadius); + image.Save(destination); + } + } + + public static void ApplyRoundedCourners(Image img, float cornerRadius) + { + var corners = BuildCorners(img.Width, img.Height, cornerRadius); + // now we have our corners time to draw them + img.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true) + { + BlenderMode = ImageSharp.PixelFormats.PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background + }); + } + + public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) + { + // first create a square + var rect = new SixLabors.Shapes.Rectangle(-0.5f, -0.5f, cornerRadius, cornerRadius); + + // then cut out of the square a circle so we are left with a corner + var cornerToptLeft = rect.Clip(new SixLabors.Shapes.Ellipse(cornerRadius-0.5f, cornerRadius - 0.5f, cornerRadius)); + + // corner is now a corner shape positions top left + //lets make 3 more positioned correctly, we cando that by translating the orgional artound the center of the image + var center = new Vector2(imageWidth / 2, imageHeight / 2); + var angle = Math.PI / 2f; + + float rightPos = imageWidth - cornerToptLeft.Bounds.Width +1; + float bottomPos = imageHeight - cornerToptLeft.Bounds.Height + 1; + + // move it across the widthof the image - the width of the shape + var cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); + var cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); + var cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); + + return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); + } + } +} \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/fb.jpg b/samples/AvatarWithRoundedCorner/fb.jpg new file mode 100644 index 000000000..7241890e2 --- /dev/null +++ b/samples/AvatarWithRoundedCorner/fb.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93bb4d6281dc1e845db57e836e0dca30b7a4062e81044efb27ad4d8b1a33130c +size 15787 diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index 44f242b3f..3841b313c 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -46,7 +46,7 @@ namespace ImageSharp public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options) where TPixel : struct, IPixel { - PngEncoder encoder = new PngEncoder(); + var encoder = new PngEncoder(); encoder.Encode(source, stream, options); return source; diff --git a/src/ImageSharp/Image/Image.FromBytes.cs b/src/ImageSharp/Image/Image.FromBytes.cs index c7309c4b1..628092359 100644 --- a/src/ImageSharp/Image/Image.FromBytes.cs +++ b/src/ImageSharp/Image/Image.FromBytes.cs @@ -126,7 +126,7 @@ namespace ImageSharp public static Image Load(Configuration config, byte[] data, IDecoderOptions options) where TPixel : struct, IPixel { - using (MemoryStream ms = new MemoryStream(data)) + using (var ms = new MemoryStream(data)) { return Load(config, ms, options); } @@ -143,7 +143,7 @@ namespace ImageSharp public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) where TPixel : struct, IPixel { - using (MemoryStream ms = new MemoryStream(data)) + using (var ms = new MemoryStream(data)) { return Load(ms, decoder, options); } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 17f7bf58f..3b72f12a8 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -63,6 +63,14 @@ TextTemplatingFileGenerator Rgba32.PixelOperations.Generated.cs + + PorterDuffFunctions.Generated.cs + TextTemplatingFileGenerator + + + DefaultPixelBlenders.Generated.cs + TextTemplatingFileGenerator + @@ -83,5 +91,15 @@ True Rgba32.PixelOperations.Generated.tt + + True + True + DefaultPixelBlenders.Generated.tt + + + True + True + PorterDuffFunctions.Generated.tt + \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs index d7f556a81..b24c96f02 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs @@ -5,6 +5,8 @@ namespace ImageSharp { + using System.Collections.Generic; + /// /// Reads and parses ICC data from a byte array /// @@ -85,9 +87,21 @@ namespace ImageSharp { IccTagTableEntry[] tagTable = this.ReadTagTable(reader); IccTagDataEntry[] entries = new IccTagDataEntry[tagTable.Length]; + var store = new Dictionary(); for (int i = 0; i < tagTable.Length; i++) { - IccTagDataEntry entry = reader.ReadTagDataEntry(tagTable[i]); + IccTagDataEntry entry; + uint offset = tagTable[i].Offset; + if (store.ContainsKey(offset)) + { + entry = store[offset]; + } + else + { + entry = reader.ReadTagDataEntry(tagTable[i]); + store.Add(offset, entry); + } + entry.TagSignature = tagTable[i].Signature; entries[i] = entry; } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index b4e5f2868..19c00e8f5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -5,6 +5,7 @@ namespace ImageSharp { + using System; using System.Collections.Generic; using System.Linq; @@ -76,30 +77,18 @@ namespace ImageSharp private IccTagTableEntry[] WriteTagData(IccDataWriter writer, List entries) { - var inData = new List(entries); - var dupData = new List(); - - while (inData.Count > 0) - { - IccTagDataEntry[] items = inData.Where(t => inData[0].Equals(t)).ToArray(); - dupData.Add(items); - foreach (IccTagDataEntry item in items) - { - inData.Remove(item); - } - } - - var table = new List(); + IEnumerable> grouped = entries.GroupBy(t => t); // (Header size) + (entry count) + (nr of entries) * (size of table entry) writer.SetIndex(128 + 4 + (entries.Count * 12)); - foreach (IccTagDataEntry[] entry in dupData) + var table = new List(); + foreach (IGrouping group in grouped) { - writer.WriteTagDataEntry(entry[0], out IccTagTableEntry tentry); - foreach (IccTagDataEntry item in entry) + writer.WriteTagDataEntry(group.Key, out IccTagTableEntry tableEntry); + foreach (IccTagDataEntry item in group) { - table.Add(new IccTagTableEntry(item.TagSignature, tentry.Offset, tentry.DataSize)); + table.Add(new IccTagTableEntry(item.TagSignature, tableEntry.Offset, tableEntry.DataSize)); } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index d8031fe6e..1e48f7181 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -57,6 +57,66 @@ namespace ImageSharp.PixelFormats /// /// Multiplies or screens the colors, depending on the source value. /// - HardLight + HardLight, + + /// + /// returns the source colors + /// + Src, + + /// + /// returns the source over the destination + /// + Atop, + + /// + /// returns the detination over the source + /// + Over, + + /// + /// the source where the desitnation and source overlap + /// + In, + + /// + /// the destination where the desitnation and source overlap + /// + Out, + + /// + /// the destination where the source does not overlap it + /// + Dest, + + /// + /// the source where they dont overlap othersie dest in overlapping parts + /// + DestAtop, + + /// + /// the destnation over the source + /// + DestOver, + + /// + /// the destination where the desitnation and source overlap + /// + DestIn, + + /// + /// the source where the desitnation and source overlap + /// + DestOut, + + /// + /// the clear. + /// + Clear, + + /// + /// clear where they overlap + /// + Xor } } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs deleted file mode 100644 index 261a98674..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Add" blending to pixels. - /// - /// The type of the pixel - internal class DefaultAddPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultAddPixelBlender Instance { get; } = new DefaultAddPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.AddFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.AddFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs deleted file mode 100644 index bca99e2f0..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Darken" blending to pixels. - /// - /// The type of the pixel - internal class DefaultDarkenPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultDarkenPixelBlender Instance { get; } = new DefaultDarkenPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.DarkenFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.DarkenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs deleted file mode 100644 index 646423cff..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Hard Light" blending to pixels. - /// - /// The type of the pixel - internal class DefaultHardLightPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultHardLightPixelBlender Instance { get; } = new DefaultHardLightPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.HardLightFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.HardLightFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs deleted file mode 100644 index 55ad81e7a..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Lighten" blending to pixels. - /// - /// The type of the pixel - internal class DefaultLightenPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultLightenPixelBlender Instance { get; } = new DefaultLightenPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.LightenFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.LightenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs deleted file mode 100644 index e21efaed0..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Multiply" blending to pixels. - /// - /// The type of the pixel - internal class DefaultMultiplyPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultMultiplyPixelBlender Instance { get; } = new DefaultMultiplyPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.MultiplyFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.MultiplyFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs deleted file mode 100644 index 9d63d11e0..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies a "Normal" otherwise nown as "Alpha Blending" blending to pixels. - /// - /// The type of the pixel - internal class DefaultNormalPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultNormalPixelBlender Instance { get; } = new DefaultNormalPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.NormalBlendFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs deleted file mode 100644 index 8172909ec..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Overlay" blending to pixels. - /// - /// The type of the pixel - internal class DefaultOverlayPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultOverlayPixelBlender Instance { get; } = new DefaultOverlayPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.OverlayFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.OverlayFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs new file mode 100644 index 000000000..915d9a924 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -0,0 +1,849 @@ +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.Memory; + + + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class DefaultPixelBlenders + where TPixel : struct, IPixel + { + + internal class Normal : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Normal Instance { get; } = new Normal(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Normal(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Normal(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Multiply : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Multiply Instance { get; } = new Multiply(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Multiply(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Multiply(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Add : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Add Instance { get; } = new Add(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Add(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Add(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Substract : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Substract Instance { get; } = new Substract(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Substract(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Substract(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Screen : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Screen Instance { get; } = new Screen(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Screen(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Screen(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Darken : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Darken Instance { get; } = new Darken(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Darken(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Darken(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Lighten : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Lighten Instance { get; } = new Lighten(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Lighten(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Lighten(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Overlay : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Overlay Instance { get; } = new Overlay(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Overlay(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Overlay(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class HardLight : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static HardLight Instance { get; } = new HardLight(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.HardLight(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.HardLight(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Src : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Src Instance { get; } = new Src(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Src(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Src(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Atop : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Atop Instance { get; } = new Atop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Atop(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Atop(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Over : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Over Instance { get; } = new Over(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Over(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Over(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class In : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static In Instance { get; } = new In(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.In(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.In(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Out : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Out Instance { get; } = new Out(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Out(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Out(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Dest : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Dest Instance { get; } = new Dest(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Dest(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Dest(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestAtop : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestAtop Instance { get; } = new DestAtop(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestAtop(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestAtop(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestOver : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestOver Instance { get; } = new DestOver(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestOver(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestOver(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestIn : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestIn Instance { get; } = new DestIn(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestIn(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestIn(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class DestOut : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static DestOut Instance { get; } = new DestOut(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DestOut(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DestOut(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Clear : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Clear Instance { get; } = new Clear(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Clear(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Clear(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + internal class Xor : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static Xor Instance { get; } = new Xor(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.Xor(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.Xor(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt new file mode 100644 index 000000000..230b05499 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -0,0 +1,118 @@ +<# +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + using ImageSharp.Memory; + + + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class DefaultPixelBlenders + where TPixel : struct, IPixel + { + +<# + + + + string[] blenders = new []{ + "Normal", + "Multiply", + "Add", + "Substract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight", + "Src" , + "Atop" , + "Over" , + "In" , + "Out" , + "Dest" , + "DestAtop" , + "DestOver" , + "DestIn" , + "DestOut" , + "Clear" , + "Xor" , + }; + + + + foreach(var blender in blenders) { +#> + internal class <#=blender#> : PixelBlender + { + + /// + /// Gets the static instance of this blender. + /// + public static <#=blender#> Instance { get; } = new <#=blender#>(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.<#=blender#>(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.<#=blender#>(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +<# + + } + +#> + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs deleted file mode 100644 index 8405c3946..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Screen" blending to pixels. - /// - /// The type of the pixel - internal class DefaultScreenPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultScreenPixelBlender Instance { get; } = new DefaultScreenPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.ScreenFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.ScreenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs deleted file mode 100644 index ab44cb760..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Applies an "Subtract" blending to pixels. - /// - /// The type of the pixel - internal class DefaultSubstractPixelBlender : PixelBlender - where TPixel : struct, IPixel - { - /// - /// Gets the static instance of this blender. - /// - public static DefaultSubstractPixelBlender Instance { get; } = new DefaultSubstractPixelBlender(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { - return PorterDuffFunctions.SubstractFunction(background, source, amount); - } - - /// - public override void Blend(Span destination, Span background, Span source, Span amount) - { - Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); - Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); - Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - - using (Buffer buffer = new Buffer(destination.Length * 3)) - { - Span destinationSpan = buffer.Slice(0, destination.Length); - Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); - Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); - - for (int i = 0; i < destination.Length; i++) - { - destinationSpan[i] = PorterDuffFunctions.SubstractFunction(backgroundSpan[i], sourceSpan[i], amount[i]); - } - - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); - } - } - } -} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs new file mode 100644 index 000000000..4213be0ba --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -0,0 +1,451 @@ +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + + internal static partial class PorterDuffFunctions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Src(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = source; + + // calculate weights + float xw = Vector4.Zero.W * source.W; + float bw = Vector4.Zero.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Atop(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + Vector4 xform = source; + + // calculate weights + float xw = backdrop.W * Vector4.Zero.W; + float bw = backdrop.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Over(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = source; + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 In(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + Vector4 xform = source; + + // calculate weights + float xw = Vector4.Zero.W * Vector4.Zero.W; + float bw = Vector4.Zero.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Out(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = Vector4.Zero.W * source.W; + float bw = Vector4.Zero.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Dest(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + Vector4 xform = backdrop; + + // calculate weights + float xw = backdrop.W * Vector4.Zero.W; + float bw = backdrop.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestAtop(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = backdrop; + + // calculate weights + float xw = Vector4.Zero.W * source.W; + float bw = Vector4.Zero.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestOver(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = backdrop; + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestIn(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + Vector4 xform = backdrop; + + // calculate weights + float xw = Vector4.Zero.W * Vector4.Zero.W; + float bw = Vector4.Zero.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DestOut(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = backdrop.W * Vector4.Zero.W; + float bw = backdrop.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Clear(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = Vector4.Zero.W * Vector4.Zero.W; + float bw = Vector4.Zero.W - xw; + float sw = Vector4.Zero.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (Vector4.Zero * bw) + (Vector4.Zero * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 Xor(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); + source.W *= opacity; + Vector4 xform = Vector4.Zero; + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Normal(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Normal(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Multiply(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Multiply(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Add(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Add(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Substract(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Substract(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Screen(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Screen(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Darken(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Darken(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Lighten(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Lighten(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Overlay(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Overlay(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLight(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(HardLight(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Src(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Src(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Atop(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Atop(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Over(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Over(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel In(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(In(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Out(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Out(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Dest(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Dest(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestAtop(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestAtop(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestOver(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestOver(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestIn(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestIn(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DestOut(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(DestOut(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Clear(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Clear(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel Xor(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(Xor(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt new file mode 100644 index 000000000..53d22d8f3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -0,0 +1,112 @@ +<# +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +// +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + + internal static partial class PorterDuffFunctions + { +<# + + void GeneratePixelBlender (string blender) + { +#> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel <#=blender#>(TPixel backdrop, TPixel source, float amount) + where TPixel : struct, IPixel + { + TPixel dest = default(TPixel); + dest.PackFromVector4(<#=blender#>(backdrop.ToVector4(), source.ToVector4(), amount)); + return dest; + } + +<# + } + + void GenerateVectorCompositor(string name, string sourceVar, string destVar, string blendVar) + { +#> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 <#=name#>(Vector4 backdrop, Vector4 source, float opacity) + { + opacity = opacity.Clamp(0, 1); +<# if(sourceVar != "Vector4.Zero" ) { #> + source.W *= opacity; +<# } #> + Vector4 xform = <#=blendVar#>; + + // calculate weights + float xw = <#=destVar#>.W * <#=sourceVar#>.W; + float bw = <#=destVar#>.W - xw; + float sw = <#=sourceVar#>.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (<#=destVar#> * bw) + (<#=sourceVar#> * sw)) / MathF.Max(a, Constants.Epsilon); + + return Vector4.Lerp(backdrop, xform, opacity); + } + +<# + } + GenerateVectorCompositor("Src", "source", "Vector4.Zero", "source"); + GenerateVectorCompositor("Atop", "Vector4.Zero", "backdrop", "source"); + GenerateVectorCompositor("Over", "source", "backdrop", "source"); + GenerateVectorCompositor("In", "Vector4.Zero", "Vector4.Zero", "source"); + GenerateVectorCompositor("Out", "source", "Vector4.Zero", "Vector4.Zero"); + GenerateVectorCompositor("Dest", "Vector4.Zero", "backdrop", "backdrop"); + GenerateVectorCompositor("DestAtop", "source", "Vector4.Zero", "backdrop"); + GenerateVectorCompositor("DestOver", "source", "backdrop", "backdrop"); + GenerateVectorCompositor("DestIn", "Vector4.Zero", "Vector4.Zero", "backdrop"); + GenerateVectorCompositor("DestOut", "Vector4.Zero", "backdrop", "Vector4.Zero"); + GenerateVectorCompositor("Clear", "Vector4.Zero", "Vector4.Zero", "Vector4.Zero"); + GenerateVectorCompositor("Xor", "source", "backdrop", "Vector4.Zero"); + + + GeneratePixelBlender("Normal"); + GeneratePixelBlender("Multiply"); + GeneratePixelBlender("Add"); + GeneratePixelBlender("Substract"); + GeneratePixelBlender("Screen"); + GeneratePixelBlender("Darken"); + GeneratePixelBlender("Lighten"); + GeneratePixelBlender("Overlay"); + GeneratePixelBlender("HardLight"); + + GeneratePixelBlender("Src"); + GeneratePixelBlender("Atop"); + GeneratePixelBlender("Over"); + GeneratePixelBlender("In"); + GeneratePixelBlender("Out"); + GeneratePixelBlender("Dest"); + GeneratePixelBlender("DestAtop"); + GeneratePixelBlender("DestOver"); + GeneratePixelBlender("DestIn"); + GeneratePixelBlender("DestOut"); + GeneratePixelBlender("Clear"); + GeneratePixelBlender("Xor"); + + +#> + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index 25eb6a5c8..b1fca9520 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -29,14 +29,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 NormalBlendFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Normal(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, source); } @@ -48,14 +43,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 MultiplyFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Multiply(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, backdrop * source); } @@ -67,14 +57,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 AddFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Add(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); } @@ -86,14 +71,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 SubstractFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Substract(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); } @@ -105,14 +85,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 ScreenFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Screen(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); } @@ -124,14 +99,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 DarkenFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Darken(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Min(backdrop, source)); } @@ -143,14 +113,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 LightenFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Lighten(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - return Compose(backdrop, source, Vector4.Max(backdrop, source)); } @@ -162,14 +127,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 OverlayFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 Overlay(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - float cr = OverlayValueFunction(backdrop.X, source.X); float cg = OverlayValueFunction(backdrop.Y, source.Y); float cb = OverlayValueFunction(backdrop.Z, source.Z); @@ -185,14 +145,9 @@ namespace ImageSharp.PixelFormats.PixelBlenders /// Opacity applied to Source Alpha /// Output color [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 HardLightFunction(Vector4 backdrop, Vector4 source, float opacity) + public static Vector4 HardLight(Vector4 backdrop, Vector4 source, float opacity) { source.W *= opacity; - if (source.W == 0) - { - return backdrop; - } - float cr = OverlayValueFunction(source.X, backdrop.X); float cg = OverlayValueFunction(source.Y, backdrop.Y); float cb = OverlayValueFunction(source.Z, backdrop.Z); @@ -222,8 +177,6 @@ namespace ImageSharp.PixelFormats.PixelBlenders [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform) { - DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); - // calculate weights float xw = backdrop.W * source.W; float bw = backdrop.W - xw; @@ -233,7 +186,7 @@ namespace ImageSharp.PixelFormats.PixelBlenders float a = xw + bw + sw; // calculate final value - xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon); xform.W = a; return xform; diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs deleted file mode 100644 index 4e829212e..000000000 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - /// - /// Collection of Porter Duff alpha blending functions - /// - /// Pixel Format - /// - /// These functions are designed to be a general solution for all color cases, - /// that is, they take in account the alpha value of both the backdrop - /// and source, and there's no need to alpha-premultiply neither the backdrop - /// nor the source. - /// Note there are faster functions for when the backdrop color is known - /// to be opaque - /// - internal static class PorterDuffFunctions - where TPixel : IPixel - { - /// - /// Source over backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.NormalBlendFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Source multiplied by backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.MultiplyFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Source added to backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.AddFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Source substracted from backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.SubstractFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Complement of source multiplied by the complement of backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.ScreenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Per element, chooses the smallest value of source and backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.DarkenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Per element, chooses the largest value of source and backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.LightenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Overlays source over backdrop - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.OverlayFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - /// - /// Hard light effect - /// - /// Backgrop color - /// Source color - /// Opacity applied to Source Alpha - /// Output color - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity) - { - return ToPixel(PorterDuffFunctions.HardLightFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TPixel ToPixel(Vector4 vector) - { - TPixel p = default(TPixel); - p.PackFromVector4(vector); - return p; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index cab357c41..5a3737dc6 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -22,25 +22,30 @@ namespace ImageSharp.PixelFormats { switch (mode) { - case PixelBlenderMode.Multiply: - return DefaultMultiplyPixelBlender.Instance; - case PixelBlenderMode.Add: - return DefaultAddPixelBlender.Instance; - case PixelBlenderMode.Substract: - return DefaultSubstractPixelBlender.Instance; - case PixelBlenderMode.Screen: - return DefaultScreenPixelBlender.Instance; - case PixelBlenderMode.Darken: - return DefaultDarkenPixelBlender.Instance; - case PixelBlenderMode.Lighten: - return DefaultLightenPixelBlender.Instance; - case PixelBlenderMode.Overlay: - return DefaultOverlayPixelBlender.Instance; - case PixelBlenderMode.HardLight: - return DefaultHardLightPixelBlender.Instance; + case PixelBlenderMode.Multiply: return DefaultPixelBlenders.Multiply.Instance; + case PixelBlenderMode.Add: return DefaultPixelBlenders.Add.Instance; + case PixelBlenderMode.Substract: return DefaultPixelBlenders.Substract.Instance; + case PixelBlenderMode.Screen: return DefaultPixelBlenders.Screen.Instance; + case PixelBlenderMode.Darken: return DefaultPixelBlenders.Darken.Instance; + case PixelBlenderMode.Lighten: return DefaultPixelBlenders.Lighten.Instance; + case PixelBlenderMode.Overlay: return DefaultPixelBlenders.Overlay.Instance; + case PixelBlenderMode.HardLight: return DefaultPixelBlenders.HardLight.Instance; + case PixelBlenderMode.Src: return DefaultPixelBlenders.Src.Instance; + case PixelBlenderMode.Atop: return DefaultPixelBlenders.Atop.Instance; + case PixelBlenderMode.Over: return DefaultPixelBlenders.Over.Instance; + case PixelBlenderMode.In: return DefaultPixelBlenders.In.Instance; + case PixelBlenderMode.Out: return DefaultPixelBlenders.Out.Instance; + case PixelBlenderMode.Dest: return DefaultPixelBlenders.Dest.Instance; + case PixelBlenderMode.DestAtop: return DefaultPixelBlenders.DestAtop.Instance; + case PixelBlenderMode.DestOver: return DefaultPixelBlenders.DestOver.Instance; + case PixelBlenderMode.DestIn: return DefaultPixelBlenders.DestIn.Instance; + case PixelBlenderMode.DestOut: return DefaultPixelBlenders.DestOut.Instance; + case PixelBlenderMode.Clear: return DefaultPixelBlenders.Clear.Instance; + case PixelBlenderMode.Xor: return DefaultPixelBlenders.Xor.Instance; + case PixelBlenderMode.Normal: default: - return DefaultNormalPixelBlender.Instance; + return DefaultPixelBlenders.Normal.Instance; } } } diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 1da69f1a8..7d7453ae3 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -35,7 +35,7 @@ namespace ImageSharp.Benchmarks for (int i = 0; i < destination.Length; i++) { - destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + destinationSpan[i] = PorterDuffFunctions.Normal(backgroundSpan[i], sourceSpan[i], amount[i]); } PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); @@ -50,7 +50,7 @@ namespace ImageSharp.Benchmarks for (int i = 0; i < destination.Length; i++) { - destination[i] = PorterDuffFunctions.NormalBlendFunction(destination[i], source[i], amount[i]); + destination[i] = PorterDuffFunctions.Normal(destination[i], source[i], amount[i]); } } diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index 23f0569e7..de10fd24f 100644 --- a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -18,14 +18,16 @@ namespace ImageSharp.Tests.Drawing .Select(x=> new object[] { x }); [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void DrawBlendedValues(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { using (var img = provider.GetImage()) { - img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); - img.Fill(NamedColors.HotPink, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); @@ -34,18 +36,72 @@ namespace ImageSharp.Tests.Drawing } [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] public void DrawBlendedValues_transparent(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { using (var img = provider.GetImage()) { - img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); - img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 40, 100), new ImageSharp.GraphicsOptions(true) + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0* scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); - img.Fill(NamedColors.Transparent, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + img.Fill(NamedColors.Transparent, new SixLabors.Shapes.Ellipse(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.DebugSave(provider, new { mode }); + } + } + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void DrawBlendedValues_transparent50Percent(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (var img = provider.GetImage()) + { + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40, 100 * scaleX, 20* scaleY)); + img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + var c = NamedColors.Red.ToVector4(); + c.W *= 0.5f; + TPixel pixel = default(TPixel); + pixel.PackFromVector4(c); + + img.Fill(pixel, new SixLabors.Shapes.Ellipse(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.DebugSave(provider, new { mode }); + } + } + + + + [Theory] + [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] + public void DrawBlendedValues_doldidEllips(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (var img = provider.GetImage()) + { + var scaleX = (img.Width / 100); + var scaleY = (img.Height / 100); + img.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40* scaleY, 100 * scaleX, 20 * scaleY)); + //img.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + //{ + // BlenderMode = mode + //}); + + img.Fill(NamedColors.Black, new SixLabors.Shapes.Ellipse(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode }); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs index 0db64c47f..34aa24fa6 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs @@ -10,10 +10,10 @@ namespace ImageSharp.Tests.Icc public class IccReaderTests { [Fact] - public void ReadProfile() + public void ReadProfile_NoEntries() { IccReader reader = CreateReader(); - + IccProfile output = reader.Read(IccTestDataProfiles.Header_Random_Array); Assert.Equal(0, output.Entries.Count); @@ -40,6 +40,18 @@ namespace ImageSharp.Tests.Icc Assert.Equal(header.Version, expected.Version); } + [Fact] + public void ReadProfile_DuplicateEntry() + { + IccReader reader = CreateReader(); + + IccProfile output = reader.Read(IccTestDataProfiles.Profile_Random_Array); + + Assert.Equal(2, output.Entries.Count); + Assert.True(ReferenceEquals(output.Entries[0], output.Entries[1])); + } + + private IccReader CreateReader() { return new IccReader(); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs index 6192e6eae..7e3f8c0c9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Tests.Icc public class IccWriterTests { [Fact] - public void WriteProfile() + public void WriteProfile_NoEntries() { IccWriter writer = CreateWriter(); @@ -23,6 +23,16 @@ namespace ImageSharp.Tests.Icc Assert.Equal(IccTestDataProfiles.Header_Random_Array, output); } + [Fact] + public void WriteProfile_DuplicateEntry() + { + IccWriter writer = CreateWriter(); + + byte[] output = writer.Write(IccTestDataProfiles.Profile_Random_Val); + + Assert.Equal(IccTestDataProfiles.Profile_Random_Array, output); + } + private IccWriter CreateWriter() { return new IccWriter(); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 45962c589..97d550592 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(NormalBlendFunctionData))] public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Normal((Vector4)back, source, amount); Assert.Equal(expected, actual); } @@ -43,7 +43,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(MultiplyFunctionData))] public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -62,7 +62,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(AddFunctionData))] public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Multiply((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -81,7 +81,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(SubstractFunctionData))] public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Substract((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -100,7 +100,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(ScreenFunctionData))] public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Screen((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -119,7 +119,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(DarkenFunctionData))] public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Darken((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -138,7 +138,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(LightenFunctionData))] public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.LightenFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Lighten((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -157,7 +157,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(OverlayFunctionData))] public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.Overlay((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } @@ -176,7 +176,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders [MemberData(nameof(HardLightFunctionData))] public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) { - Vector4 actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + Vector4 actual = PorterDuffFunctions.HardLight((Vector4)back, source, amount); VectorAssert.Equal(expected, actual, 5); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 8932f1ffe..b2a663d07 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Normal((TPixel)(TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -41,7 +41,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void NormalBlendFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultNormalPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Normal().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -51,7 +51,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultNormalPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Normal().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -71,7 +71,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void MultiplyFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Multiply((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -80,7 +80,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void MultiplyFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultMultiplyPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Multiply().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -90,7 +90,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultMultiplyPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Multiply().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -110,7 +110,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.AddFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Add((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -119,7 +119,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void AddFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultAddPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Add().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -129,7 +129,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultAddPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Add().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -149,7 +149,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void SubstractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Substract((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -158,7 +158,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void SubstractFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultSubstractPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Substract().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -168,7 +168,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultSubstractPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Substract().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -188,7 +188,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void ScreenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Screen((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -197,7 +197,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void ScreenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultScreenPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Screen().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -207,7 +207,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultScreenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Screen().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -227,7 +227,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void DarkenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Darken((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -236,7 +236,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void DarkenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultDarkenPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Darken().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -246,7 +246,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultDarkenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Darken().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -266,7 +266,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void LightenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.LightenFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Lighten((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -275,7 +275,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void LightenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultLightenPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Lighten().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -285,7 +285,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultLightenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Lighten().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -305,7 +305,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void OverlayFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.Overlay((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -314,7 +314,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void OverlayFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultOverlayPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.Overlay().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -324,7 +324,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultOverlayPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.Overlay().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -344,7 +344,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void HardLightFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + TPixel actual = PorterDuffFunctions.HardLight((TPixel)back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -353,7 +353,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders public void HardLightFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) where TPixel : struct, IPixel { - TPixel actual = new DefaultHardLightPixelBlender().Blend(back, source, amount); + TPixel actual = new DefaultPixelBlenders.HardLight().Blend(back, source, amount); VectorAssert.Equal(expected, actual, 2); } @@ -363,7 +363,7 @@ namespace ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { Span dest = new Span(new TPixel[1]); - new DefaultHardLightPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.HardLight().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index ce81499ed..6bcada0ff 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -15,27 +15,55 @@ namespace ImageSharp.Tests.PixelFormats public partial class PixelOperationsTests { + + public static TheoryData BlenderMappings = new TheoryData() { - { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, - { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, - { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, - { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, - { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, - { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, - { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, - { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, - - { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, - { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, - { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, - { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, - { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, - { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, - { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, - { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, - { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, + { new TestPixel(), typeof(DefaultPixelBlenders.Normal), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultPixelBlenders.Screen), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultPixelBlenders.HardLight), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultPixelBlenders.Overlay), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultPixelBlenders.Darken), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultPixelBlenders.Lighten), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultPixelBlenders.Add), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultPixelBlenders.Substract), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultPixelBlenders.Multiply), PixelBlenderMode.Multiply }, + + { new TestPixel(), typeof(DefaultPixelBlenders.Src), PixelBlenderMode.Src }, + { new TestPixel(), typeof(DefaultPixelBlenders.Atop), PixelBlenderMode.Atop }, + { new TestPixel(), typeof(DefaultPixelBlenders.Over), PixelBlenderMode.Over }, + { new TestPixel(), typeof(DefaultPixelBlenders.In), PixelBlenderMode.In }, + { new TestPixel(), typeof(DefaultPixelBlenders.Out), PixelBlenderMode.Out }, + { new TestPixel(), typeof(DefaultPixelBlenders.Dest), PixelBlenderMode.Dest }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestAtop), PixelBlenderMode.DestAtop }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOver), PixelBlenderMode.DestOver }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestIn), PixelBlenderMode.DestIn }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOut), PixelBlenderMode.DestOut }, + { new TestPixel(), typeof(DefaultPixelBlenders.Clear), PixelBlenderMode.Clear }, + { new TestPixel(), typeof(DefaultPixelBlenders.Xor), PixelBlenderMode.Xor }, + + { new TestPixel(), typeof(DefaultPixelBlenders.Normal), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultPixelBlenders.Screen), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultPixelBlenders.HardLight), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultPixelBlenders.Overlay), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultPixelBlenders.Darken), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultPixelBlenders.Lighten), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultPixelBlenders.Add), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultPixelBlenders.Substract), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultPixelBlenders.Multiply), PixelBlenderMode.Multiply }, + { new TestPixel(), typeof(DefaultPixelBlenders.Src), PixelBlenderMode.Src }, + { new TestPixel(), typeof(DefaultPixelBlenders.Atop), PixelBlenderMode.Atop }, + { new TestPixel(), typeof(DefaultPixelBlenders.Over), PixelBlenderMode.Over }, + { new TestPixel(), typeof(DefaultPixelBlenders.In), PixelBlenderMode.In }, + { new TestPixel(), typeof(DefaultPixelBlenders.Out), PixelBlenderMode.Out }, + { new TestPixel(), typeof(DefaultPixelBlenders.Dest), PixelBlenderMode.Dest }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestAtop), PixelBlenderMode.DestAtop }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOver), PixelBlenderMode.DestOver }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestIn), PixelBlenderMode.DestIn }, + { new TestPixel(), typeof(DefaultPixelBlenders.DestOut), PixelBlenderMode.DestOut }, + { new TestPixel(), typeof(DefaultPixelBlenders.Clear), PixelBlenderMode.Clear }, + { new TestPixel(), typeof(DefaultPixelBlenders.Xor), PixelBlenderMode.Xor }, + }; [Theory] diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 20fff50a8..32a4a8e57 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -10,83 +10,124 @@ namespace ImageSharp.Tests { internal static class IccTestDataProfiles { - public static readonly IccProfileHeader Header_Random_Write = new IccProfileHeader + public static readonly IccProfileHeader Header_Random_Write = CreateHeaderRandomValue( + 562, // should be overwritten + new IccProfileId(1, 2, 3, 4), // should be overwritten + "ijkl"); // should be overwritten to "acsp" + + public static readonly IccProfileHeader Header_Random_Read = CreateHeaderRandomValue(132, +#if !NETSTANDARD1_1 + new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), +#else + IccProfileId.Zero, +#endif + "acsp"); + + public static readonly byte[] Header_Random_Array = CreateHeaderRandomArray(132, 0, new byte[] { - Class = IccProfileClass.DisplayDevice, - CmmType = "abcd", - CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), - CreatorSignature = "dcba", - DataColorSpace = IccColorSpaceType.Rgb, - DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, - DeviceManufacturer = 123456789u, - DeviceModel = 987654321u, - FileSignature = "ijkl", // should be overwritten to "acsp" - Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, - Id = new IccProfileId(1, 2, 3, 4), // should be overwritten - PcsIlluminant = new Vector3(4, 5, 6), - PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, - ProfileConnectionSpace = IccColorSpaceType.CieXyz, - RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, - Size = 562, // should be overwritten - Version = new Version(4, 3, 0), - }; +#if !NETSTANDARD1_1 + 0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12, +#else + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif + }); - public static readonly IccProfileHeader Header_Random_Read = new IccProfileHeader + public static IccProfileHeader CreateHeaderRandomValue(uint size, IccProfileId id, string fileSignature) { - Class = IccProfileClass.DisplayDevice, - CmmType = "abcd", - CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), - CreatorSignature = "dcba", - DataColorSpace = IccColorSpaceType.Rgb, - DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, - DeviceManufacturer = 123456789u, - DeviceModel = 987654321u, - FileSignature = "acsp", - Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, + return new IccProfileHeader + { + Class = IccProfileClass.DisplayDevice, + CmmType = "abcd", + CreationDate = new DateTime(1990, 11, 26, 7, 21, 42), + CreatorSignature = "dcba", + DataColorSpace = IccColorSpaceType.Rgb, + DeviceAttributes = IccDeviceAttribute.ChromaBlackWhite | IccDeviceAttribute.OpacityTransparent, + DeviceManufacturer = 123456789u, + DeviceModel = 987654321u, + FileSignature = "acsp", + Flags = IccProfileFlag.Embedded | IccProfileFlag.Independent, #if !NETSTANDARD1_1 - Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), + Id = new IccProfileId(2931428592, 418415738, 3086756963, 2237536530), #else Id = IccProfileId.Zero, #endif - PcsIlluminant = new Vector3(4, 5, 6), - PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, - ProfileConnectionSpace = IccColorSpaceType.CieXyz, - RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, - Size = 132, - Version = new Version(4, 3, 0), - }; + PcsIlluminant = new Vector3(4, 5, 6), + PrimaryPlatformSignature = IccPrimaryPlatformType.MicrosoftCorporation, + ProfileConnectionSpace = IccColorSpaceType.CieXyz, + RenderingIntent = IccRenderingIntent.AbsoluteColorimetric, + Size = size, + Version = new Version(4, 3, 0), + }; + } - public static readonly byte[] Header_Random_Array = + public static byte[] CreateHeaderRandomArray(uint size, uint nrOfEntries, byte[] profileId) { - 0x00, 0x00, 0x00, 0x84, // Size (132) - 0x61, 0x62, 0x63, 0x64, // CmmType - 0x04, 0x30, 0x00, 0x00, // Version - 0x6D, 0x6E, 0x74, 0x72, // Class - 0x52, 0x47, 0x42, 0x20, // DataColorSpace - 0x58, 0x59, 0x5A, 0x20, // ProfileConnectionSpace - 0x07, 0xC6, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x07, 0x00, 0x15, 0x00, 0x2A, // CreationDate - 0x61, 0x63, 0x73, 0x70, // FileSignature - 0x4D, 0x53, 0x46, 0x54, // PrimaryPlatformSignature - 0x00, 0x00, 0x00, 0x01, // Flags - 0x07, 0x5B, 0xCD, 0x15, // DeviceManufacturer - 0x3A, 0xDE, 0x68, 0xB1, // DeviceModel - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, // DeviceAttributes - 0x00, 0x00, 0x00, 0x03, // RenderingIntent - 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // PcsIlluminant - 0x64, 0x63, 0x62, 0x61, // CreatorSignature + return ArrayHelper.Concat( + new byte[] + { + (byte)(size >> 24), (byte)(size >> 16), (byte)(size >> 8), (byte)size, // Size + 0x61, 0x62, 0x63, 0x64, // CmmType + 0x04, 0x30, 0x00, 0x00, // Version + 0x6D, 0x6E, 0x74, 0x72, // Class + 0x52, 0x47, 0x42, 0x20, // DataColorSpace + 0x58, 0x59, 0x5A, 0x20, // ProfileConnectionSpace + 0x07, 0xC6, 0x00, 0x0B, 0x00, 0x1A, 0x00, 0x07, 0x00, 0x15, 0x00, 0x2A, // CreationDate + 0x61, 0x63, 0x73, 0x70, // FileSignature + 0x4D, 0x53, 0x46, 0x54, // PrimaryPlatformSignature + 0x00, 0x00, 0x00, 0x01, // Flags + 0x07, 0x5B, 0xCD, 0x15, // DeviceManufacturer + 0x3A, 0xDE, 0x68, 0xB1, // DeviceModel + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, // DeviceAttributes + 0x00, 0x00, 0x00, 0x03, // RenderingIntent + 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // PcsIlluminant + 0x64, 0x63, 0x62, 0x61, // CreatorSignature + }, + profileId, + new byte[] + { + // Padding + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + // Nr of tag table entries (0) + (byte)(nrOfEntries >> 24), (byte)(nrOfEntries >> 16), (byte)(nrOfEntries >> 8), (byte)nrOfEntries + }); + } + + public static byte[] Profile_Random_Array = ArrayHelper.Concat(CreateHeaderRandomArray(168, 2, new byte[] + { +#if !NETSTANDARD1_1 + 0xA9, 0x71, 0x8F, 0xC1, 0x1E, 0x2D, 0x64, 0x1B, 0x10, 0xF4, 0x7D, 0x6A, 0x5B, 0xF6, 0xAC, 0xB9 +#else + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif + }), + new byte[] + { + 0x00, 0x00, 0x00, 0x00, // tag signature (Unknown) + 0x00, 0x00, 0x00, 0x9C, // tag offset (156) + 0x00, 0x00, 0x00, 0x0C, // tag size (12) + + 0x00, 0x00, 0x00, 0x00, // tag signature (Unknown) + 0x00, 0x00, 0x00, 0x9C, // tag offset (156) + 0x00, 0x00, 0x00, 0x0C, // tag size (12) + }, + IccTestDataTagDataEntry.TagDataEntryHeader_UnknownArr, + IccTestDataTagDataEntry.Unknown_Arr + ); + public static IccProfile Profile_Random_Val = new IccProfile(CreateHeaderRandomValue(168, #if !NETSTANDARD1_1 - 0xAE, 0xBA, 0x0C, 0xF0, 0x18, 0xF0, 0x84, 0x7A, 0xB7, 0xFC, 0x2C, 0x63, 0x85, 0x5E, 0x19, 0x12, // Id + new IccProfileId(0xA9718FC1, 0x1E2D641B, 0x10F47D6A, 0x5BF6ACB9), #else - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Id + IccProfileId.Zero, #endif - // Padding - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - // Nr of tag table entries (0) - 0x00, 0x00, 0x00, 0x00, - }; + "acsp"), + new IccTagDataEntry[] + { + IccTestDataTagDataEntry.Unknown_Val, + IccTestDataTagDataEntry.Unknown_Val + }); } }