diff --git a/.gitignore b/.gitignore index 8034c8c89..c2e6f7d53 100644 --- a/.gitignore +++ b/.gitignore @@ -158,7 +158,6 @@ AppPackages/ # Others *.[Cc]ache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl @@ -202,7 +201,8 @@ FakesAssemblies/ **/node_modules **/node_modules/* -**/TestOutput +**/Images/ActualOutput +**/Images/ReferenceOutput # ASP.NET 5 project.lock.json @@ -218,3 +218,4 @@ artifacts/ **/CodeCoverage/* docs/ /samples/AvatarWithRoundedCorner/output +/ImageSharp.Coverage.xml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e7972649f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "tests/Images/External"] + path = tests/Images/External + url = https://github.com/SixLabors/Imagesharp.Tests.Images.git + branch = master diff --git a/.travis.yml b/.travis.yml index 4ae163530..a4f68b1d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ branches: - coverity_scan script: + - git submodule -q update --init - dotnet restore - dotnet test tests/ImageSharp.Tests/ImageSharp.Tests.csproj -c Release -f "netcoreapp1.1" diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..c9c7453f6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceRoot}/samples/AvatarWithRoundedCorner/bin/Debug/netcoreapp1.1/AvatarWithRoundedCorner.dll", + "args": [], + "cwd": "${workspaceRoot}/samples/AvatarWithRoundedCorner", + // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window + "console": "internalConsole", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/ImageSharp.ruleset b/ImageSharp.ruleset index 0bd9cd11a..2149364b1 100644 --- a/ImageSharp.ruleset +++ b/ImageSharp.ruleset @@ -2,10 +2,10 @@ - - - + \ No newline at end of file diff --git a/ImageSharp.sln b/ImageSharp.sln index a004f1371..4ea89dd45 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,6 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 +VisualStudioVersion = 15.0.26730.3 VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" @@ -32,28 +33,28 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E574A07-F879-4811-9C41-5CBDC6BAFDB7}" ProjectSection(SolutionItems) = preProject src\Shared\AssemblyInfo.Common.cs = src\Shared\AssemblyInfo.Common.cs - src\Shared\stylecop.json = src\Shared\stylecop.json EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Drawing", "src\ImageSharp.Drawing\ImageSharp.Drawing.csproj", "{2E33181E-6E28-4662-A801-E2E7DC206029}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "build", "build\build.csproj", "{575A5002-DD9F-4335-AA47-1DD87FA13645}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\ImageSharp.Tests\ImageSharp.Tests.csproj", "{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Benchmarks", "tests\ImageSharp.Benchmarks\ImageSharp.Benchmarks.csproj", "{2BF743D8-2A06-412D-96D7-F448F00C5EA5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Sandbox46", "tests\ImageSharp.Sandbox46\ImageSharp.Sandbox46.csproj", "{561B880A-D9EE-44EF-90F5-817C54A9D9AB}" EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 @@ -87,18 +88,6 @@ Global {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x64.Build.0 = Release|Any CPU {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x86.ActiveCfg = Release|Any CPU {2E33181E-6E28-4662-A801-E2E7DC206029}.Release|x86.Build.0 = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|Any CPU.Build.0 = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x64.ActiveCfg = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x64.Build.0 = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x86.ActiveCfg = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Debug|x86.Build.0 = Debug|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|Any CPU.ActiveCfg = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|Any CPU.Build.0 = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x64.ActiveCfg = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x64.Build.0 = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x86.ActiveCfg = Release|Any CPU - {575A5002-DD9F-4335-AA47-1DD87FA13645}.Release|x86.Build.0 = Release|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -123,18 +112,6 @@ Global {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x64.Build.0 = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.ActiveCfg = Release|Any CPU {2BF743D8-2A06-412D-96D7-F448F00C5EA5}.Release|x86.Build.0 = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x64.ActiveCfg = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x64.Build.0 = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x86.ActiveCfg = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|x86.Build.0 = Debug|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.Build.0 = Release|Any CPU - {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|x64.ActiveCfg = Release|Any CPU - {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 @@ -159,6 +136,18 @@ Global {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.ActiveCfg = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x64.Build.0 = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x86.ActiveCfg = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Debug|x86.Build.0 = Debug|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|Any CPU.Build.0 = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x64.ActiveCfg = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x64.Build.0 = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x86.ActiveCfg = Release|Any CPU + {561B880A-D9EE-44EF-90F5-817C54A9D9AB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -167,11 +156,13 @@ Global {9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {2E33181E-6E28-4662-A801-E2E7DC206029} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} - {575A5002-DD9F-4335-AA47-1DD87FA13645} = {E919DF0B-2607-4462-8FC0-5C98FE50F8C9} {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} {07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2} + {561B880A-D9EE-44EF-90F5-817C54A9D9AB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} EndGlobalSection EndGlobal diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index b058fad4e..1839bf2f8 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -342,6 +342,7 @@ True AC DC + DCT EOF FDCT IDCT diff --git a/README.md b/README.md index 7113d6ba1..31ca5cef3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and emb > **ImageSharp** has made excellent progress and contains many great features but is still considered by us to be still in early stages (alpha). As such, we cannot support its use on production environments until the library reaches release candidate status. > -> Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp). +> Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/sixlabors). [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/SixLabors/ImageSharp/master/APACHE-2.0-LICENSE.txt) [![GitHub issues](https://img.shields.io/github/issues/SixLabors/ImageSharp.svg)](https://github.com/SixLabors/ImageSharp/issues) @@ -31,20 +31,20 @@ At present the code is pre-release but when ready it will be available on [Nuget **Pre-release downloads** -We already have a [MyGet package repository](https://www.myget.org/gallery/imagesharp) - for bleeding-edge / development NuGet releases. +We already have a [MyGet package repository](https://www.myget.org/gallery/sixlabors) - for bleeding-edge / development NuGet releases. ### Packages The **ImageSharp** library is made up of multiple packages. Packages include: -- **ImageSharp** +- **SixLabors.ImageSharp** - Contains the generic `Image` class, PixelFormats, Primitives, Configuration, and other core functionality. - The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats. - Transform methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image. - Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions. -- **ImageSharp.Drawing** +- **SixLabors.ImageSharp.Drawing** - Brushes and various drawing algorithms, including drawing images. - Various vector drawing methods for drawing paths, polygons etc. - Text drawing. @@ -88,9 +88,10 @@ On platforms supporting netstandard 1.3+ // Image.Load(string path) is a shortcut for our default type. Other pixel formats use Image.Load(string path)) using (Image image = Image.Load("foo.jpg")) { - image.Resize(image.Width / 2, image.Height / 2) - .Grayscale() - .Save("bar.jpg"); // automatic encoder selected based on extension. + image.Mutate(x => x + .Resize(image.Width / 2, image.Height / 2) + .Grayscale()); + image.Save("bar.jpg"); // automatic encoder selected based on extension. } ``` on netstandard 1.1 - 1.2 @@ -100,9 +101,10 @@ using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) using (Image image = Image.Load(stream)) { - image.Resize(image.Width / 2, image.Height / 2) - .Grayscale() - .Save(output); + image.Mutate(x => x + .Resize(image.Width / 2, image.Height / 2) + .Grayscale()); + image.Save(output); } ``` @@ -116,7 +118,7 @@ using (Image image = new Image(400, 400)) } ``` -For optimized access within a loop it is recommended that the following methods are used. +For optimized synchronous access within a loop it is recommended that the following methods are used. 1. `image.GetRowSpan(y)` 2. `image.GetRowSpan(x, y)` @@ -140,9 +142,7 @@ Grand High Eternal Dictator Core Team - [Dirk Lemstra](https://github.com/dlemstra) -- [Jeavon Leopold](https://github.com/jeavon) - [Anton Firsov](https://github.com/antonfirsov) -- [Olivia Ifrim](https://github.com/olivif) - [Scott Williams](https://github.com/tocsoft) ### Backers diff --git a/appveyor.yml b/appveyor.yml index fdbe46b01..5c548a71c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,8 +4,13 @@ image: Visual Studio 2017 # prevent the double build when a branch has an active PR skip_branch_with_pr: true -init: - - ps: iex ((new-object net.webclient).DownloadString('https://gist.githubusercontent.com/PureKrome/0f79e25693d574807939/raw/8cf3160c9516ef1f4effc825c0a44acc918a0b5a/appveyor-build-info.ps')) +install: + - choco install gitversion.portable -pre -y + +before_build: + - git submodule -q update --init + - cmd: dotnet --version + - ps: c:\ProgramData\chocolatey\lib\gitversion.portable\tools\gitversion.exe /l console /output buildserver build_script: - cmd: build.cmd @@ -13,16 +18,17 @@ build_script: test_script: - tests\CodeCoverage\CodeCoverage.cmd -artifacts: -- path: artifacts\bin\ImageSharp\**\*.nupkg +after_test: + - cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.%GitVersion_NuGetVersion%.nupkg" + - cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.Drawing.%GitVersion_NuGetVersion%.nupkg" deploy: # MyGet Deployment for builds & releases - - provider: NuGet - server: https://www.myget.org/F/imagesharp/api/v2/package - symbol_server: https://www.myget.org/F/imagesharp/symbols/api/v2/package - api_key: - secure: P2Fz82nty+itjL+kNRCsMQcqzngmVtkU0R4CZqgST7zgUaE6/1q9ekh5MKKlZLkD - artifact: /.*\.nupkg/ - on: - branch: master + - provider: NuGet + server: https://www.myget.org/F/sixlabors/api/v2/package + symbol_server: https://www.myget.org/F/sixlabors/symbols/api/v2/package + api_key: + secure: V/lEHP0UeMWIpWd0fiNlY2IgbCnJKQlGdRksECdJbOBdaE20Fl0RNL7WyqHe02o4 + artifact: /.*\.nupkg/ + on: + branch: master diff --git a/build.cmd b/build.cmd index e33a230bc..1ba1b3742 100644 --- a/build.cmd +++ b/build.cmd @@ -1,2 +1,39 @@ @echo Off -call build\build.cmd \ No newline at end of file + +SET versionCommand= +if not "%GitVersion_NuGetVersion%" == "" ( + SET versionCommand=/p:packageversion=%GitVersion_NuGetVersion% + @echo building with version set to '%GitVersion_NuGetVersion%' +) + +dotnet restore %versionCommand% + +ECHO Building projects +dotnet build -c Release %versionCommand% + +if not "%errorlevel%"=="0" goto failure + +if not "%CI%" == "True" ( + ECHO NOT on CI server running tests + dotnet test ./tests/ImageSharp.Tests/ImageSharp.Tests.csproj --no-build -c Release +) +if not "%errorlevel%"=="0" goto failure + +ECHO Packaging projects +dotnet pack ./src/ImageSharp/ -c Release --output ../../artifacts --no-build %versionCommand% +if not "%errorlevel%"=="0" goto failure + +dotnet pack ./src/ImageSharp.Drawing/ -c Release --output ../../artifacts --no-build %versionCommand% +if not "%errorlevel%"=="0" goto failure + +:success +ECHO successfully built project +REM exit 0 +goto end + +:failure +ECHO failed to build. +REM exit -1 +goto end + +:end \ No newline at end of file diff --git a/build/Program.cs b/build/Program.cs deleted file mode 100644 index 4e59b2214..000000000 --- a/build/Program.cs +++ /dev/null @@ -1,463 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ConsoleApplication -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text; - using System.Xml; - using LibGit2Sharp; - using Microsoft.Build.Construction; - using Microsoft.Build.Evaluation; - using NuGet.Versioning; - - /// - /// This updates the version numbers for all the projects in the src folder. - /// The version number it will geneate is dependent on if this is a build from master or a branch/PR - /// - /// If its a build on master - /// We take the version number specified in project.json, - /// count how meny commits the repo has had that will affect this project or its dependencies since the version number of manually changed - /// If this is the first commit that effected this project since number change then leave the version number as defined i.e. will build 1.0.0 if thats in project.json - /// unless it is a preview build number in which case we always add the counter - /// - /// If the build is from a PR/branch - /// We take the version number specified in project.json, append a tag for the branch/PR (so we can determin how each package was built) - /// append number of commits effecting the project. - /// - /// - /// - /// for PR#123 and project.json version 2.0.1 and we have had 30 commits affecting the project - /// we would end up with version number 2.0.1-PR124-00030 - /// - /// for branch `fix-stuff` project.json version 2.0.1-alpha1 and we have had 832 commits affecting the project - /// we would end up with version number 2.0.1-alpha1-fix-stuff-00832 - /// - /// for `master` project.json version 2.0.1-alpha1 and we have had 832 commits affecting the project - /// we would end up with version number 2.0.1-alpha1-00832 - /// - /// for `master` project.json version 2.0.1 and we have had 132 commits affecting the project - /// we would end up with version number 2.0.1-CI-00132 - /// - /// for `master` project.json version 2.0.1 and we have had 1 commits affecting the project - /// we would end up with version number 2.0.1 - /// - /// for `master` project.json version 2.0.1-alpha1 and we have had 1 commits affecting the project - /// we would end up with version number 2.0.1-alpha1 - /// - /// - /// TODO Add the option for using this to update the version numbers in a project and its dependent references. - /// - public class Program - { - private const string FallbackTag = "CI"; - - /// - /// Main entry point. - /// - /// The arguments. - public static void Main(string[] args) - { - bool resetmode = args.Contains("reset"); - - // Find the project root - string root = Path.GetFullPath(Path.Combine(LibGit2Sharp.Repository.Discover("."), "..")); - - // Lets find the repo - Repository repo = new LibGit2Sharp.Repository(root); - - // Lets find all the project.json files in the src folder (don't care about versioning `tests`) - IEnumerable projectFiles = Directory.EnumerateFiles(Path.Combine(root, "src"), "*.csproj", SearchOption.AllDirectories); - - ResetProject(projectFiles); - - // Open them and convert them to source projects - List projects = projectFiles.Select(x => ProjectRootElement.Open(x, ProjectCollection.GlobalProjectCollection, true)) - .Select(x => new SourceProject(x, repo.Info.WorkingDirectory)) - .ToList(); - - if (!resetmode) - { - CaclulateProjectVersionNumber(projects, repo); - - UpdateVersionNumbers(projects); - - CreateBuildScript(projects, root); - - foreach (SourceProject p in projects) - { - Console.WriteLine($"{p.Name} {p.FinalVersionNumber}"); - } - } - } - - private static void CreateBuildScript(IEnumerable projects, string root) - { - string outputDir = Path.GetFullPath(Path.Combine(root, @"artifacts\bin\ImageSharp")); - - StringBuilder sb = new StringBuilder(); - foreach (SourceProject p in projects) - { - sb.AppendLine($@"dotnet pack --configuration Release --output ""{outputDir}"" ""{p.ProjectFilePath}"""); - } - - File.WriteAllText("build-inner.cmd", sb.ToString()); - } - - private static void UpdateVersionNumbers(IEnumerable projects) - { - foreach (SourceProject p in projects) - { - // create a backup file so we can rollback later without breaking formatting - File.Copy(p.FullProjectFilePath, $"{p.FullProjectFilePath}.bak", true); - } - - foreach (SourceProject p in projects) - { - // TODO force update of all dependent projects to point to the newest build. - // we skip the build number and standard CI prefix on first commits - string newVersion = p.FinalVersionNumber; - - p.UpdateVersion(newVersion); - } - } - - private static string CurrentBranch(Repository repo) - { - // lets build version friendly commit - string branch = repo.Head.FriendlyName; - - // lets see if we are running in appveyor and if we are use the environment variables instead of the head - string appveryorBranch = Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH"); - if (!string.IsNullOrWhiteSpace(appveryorBranch)) - { - branch = appveryorBranch; - } - - string prNumber = Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER"); - if (!string.IsNullOrWhiteSpace(prNumber)) - { - branch = $"PR{int.Parse(prNumber):000}"; - } - - // this will happen when checking out a comit directly and not a branch (like appveryor does when it builds) - if (branch == "(no branch)") - { - throw new Exception("unable to find branch"); - } - - // clean branch names (might need to be improved) - branch = branch.Replace("/", "-").Replace("--", "-"); - - return branch; - } - - private static void CaclulateProjectVersionNumber(List projects, Repository repo) - { - string branch = CurrentBranch(repo); - - // populate the dependency chains - projects.ForEach(x => x.PopulateDependencies(projects)); - - // update the final version based on the repo history and the currentr branch name - projects.ForEach(x => x.CalculateVersion(repo, branch)); - } - - private static void ResetProject(IEnumerable projectPaths) - { - if (File.Exists("build-inner.cmd")) - { - File.Delete("build-inner.cmd"); - } - - // revert the project.json change be reverting it but skipp all the git stuff as its not needed - foreach (string p in projectPaths) - { - if (File.Exists($"{p}.bak")) - { - File.Copy($"{p}.bak", p, true); - File.Delete($"{p}.bak"); - } - } - } - - /// - /// Project level logic - /// - public class SourceProject - { - private readonly IEnumerable dependencies; - private readonly ProjectRootElement project; - - /// - /// Initializes a new instance of the class. - /// - /// The project. - /// The root. - public SourceProject(ProjectRootElement project, string root) - { - this.Name = project.Properties.FirstOrDefault(x => x.Name == "AssemblyTitle").Value; - - this.ProjectDirectory = project.DirectoryPath.Substring(root.Length); - this.ProjectFilePath = project.ProjectFileLocation.File.Substring(root.Length); - this.FullProjectFilePath = Path.GetFullPath(project.ProjectFileLocation.File); - this.Version = new NuGetVersion(project.Properties.FirstOrDefault(x => x.Name == "VersionPrefix").Value); - this.dependencies = project.Items.Where(x => x.ItemType == "ProjectReference").Select(x => Path.GetFullPath(Path.Combine(project.DirectoryPath, x.Include))); - this.FinalVersionNumber = this.Version.ToFullString(); - this.project = project; - } - - /// - /// Gets the project directory. - /// - /// - /// The project directory. - /// - public string ProjectDirectory { get; } - - /// - /// Gets the version. - /// - /// - /// The version. - /// - public NuGetVersion Version { get; private set; } - - /// - /// Gets the dependent projects. - /// - /// - /// The dependent projects. - /// - public List DependentProjects { get; private set; } - - /// - /// Gets the name. - /// - /// - /// The name. - /// - public string Name { get; private set; } - - /// - /// Gets the project file path. - /// - /// - /// The project file path. - /// - public string ProjectFilePath { get; private set; } - - /// - /// Gets the commit count since version change. - /// - /// - /// The commit count since version change. - /// - public int CommitCountSinceVersionChange { get; private set; } = 0; - - /// - /// Gets the full project file path. - /// - /// - /// The full project file path. - /// - public string FullProjectFilePath { get; private set; } - - /// - /// Gets the final version number. - /// - /// - /// The final version number. - /// - public string FinalVersionNumber { get; private set; } - - /// - /// Populates the dependencies. - /// - /// The projects. - public void PopulateDependencies(IEnumerable projects) - { - this.DependentProjects = projects.Where(x => this.dependencies.Contains(x.FullProjectFilePath)).ToList(); - } - - /// - /// Update the version number in the project file - /// - /// the new version number to save. - internal void UpdateVersion(string versionnumber) - { - this.project.AddProperty("VersionPrefix", versionnumber); - this.Version = new NuGetVersion(versionnumber); - this.project.Save(); - } - - /// - /// Calculates the version. - /// - /// The repo. - /// The branch. - internal void CalculateVersion(Repository repo, string branch) - { - foreach (Commit c in repo.Commits) - { - if (!this.ApplyCommit(c, repo)) - { - // we have finished lets populate the final version number - this.FinalVersionNumber = this.CalculateVersionNumber(branch); - - return; - } - } - } - - private bool MatchPath(string path) - { - if (path.StartsWith(this.ProjectDirectory, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (this.DependentProjects.Any()) - { - return this.DependentProjects.Any(x => x.MatchPath(path)); - } - - return false; - } - - private bool ApplyCommitInternal(Commit commit, TreeChanges changes, Repository repo) - { - this.CommitCountSinceVersionChange++; - - // return false if this is a version number root - TreeEntryChanges projectFileChange = changes.Where(x => x.Path?.Equals(this.ProjectFilePath, StringComparison.OrdinalIgnoreCase) == true).FirstOrDefault(); - if (projectFileChange != null) - { - if (projectFileChange.Status == ChangeKind.Added) - { - // the version must have been set here - return false; - } - else - { - Blob blob = repo.Lookup(projectFileChange.Oid); - using (Stream s = blob.GetContentStream()) - { - using (XmlReader reader = XmlReader.Create(s)) - { - ProjectRootElement proj = ProjectRootElement.Create(reader); - NuGetVersion version = new NuGetVersion(proj.Properties.FirstOrDefault(x => x.Name == "VersionPrefix").Value); - if (version != this.Version) - { - // version changed - return false; - } - } - } - } - - // version must have been the same lets carry on - return true; - } - - return true; - } - - private bool ApplyCommit(Commit commit, Repository repo) - { - foreach (Commit parent in commit.Parents) - { - TreeChanges changes = repo.Diff.Compare(parent.Tree, commit.Tree); - - foreach (TreeEntryChanges change in changes) - { - if (!string.IsNullOrWhiteSpace(change.OldPath)) - { - if (this.MatchPath(change.OldPath)) - { - return this.ApplyCommitInternal(commit, changes, repo); - } - } - - if (!string.IsNullOrWhiteSpace(change.Path)) - { - if (this.MatchPath(change.Path)) - { - return this.ApplyCommitInternal(commit, changes, repo); - } - } - } - } - - return true; - } - - private string CalculateVersionNumber(string branch) - { - string version = this.Version.ToFullString(); - - // master only - if (this.CommitCountSinceVersionChange == 1 && branch == "master") - { - if (this.Version.IsPrerelease) - { - // prerelease always needs the build counter just not on a branch name - return $"{version}-{this.CommitCountSinceVersionChange:00000}"; - } - - // this is the full release happy path, first commit after changing the version number - return version; - } - - string rootSpecialVersion = string.Empty; - - if (this.Version.IsPrerelease) - { - // probably a much easy way for doing this but it work sell enough for a build script - string[] parts = version.Split(new[] { '-' }, 2); - version = parts[0]; - rootSpecialVersion = parts[1]; - } - - // if master and the version doesn't manually specify a prerelease tag force one on for CI builds - if (branch == "master") - { - if (!this.Version.IsPrerelease) - { - branch = FallbackTag; - } - else - { - branch = string.Empty; - } - } - - if (rootSpecialVersion.Length > 0) - { - rootSpecialVersion = "-" + rootSpecialVersion; - } - - if (branch.Length > 0) - { - branch = "-" + branch; - } - - int maxLength = 20; // dotnet will fail to populate the package if the tag is > 20 - maxLength -= rootSpecialVersion.Length; // this is a required tag - maxLength -= 7; // for the counter and dashes - - if (branch.Length > maxLength) - { - branch = branch.Substring(0, maxLength); - } - - return $"{version}{rootSpecialVersion}{branch}-{this.CommitCountSinceVersionChange:00000}"; - } - } - } -} diff --git a/build/Properties/launchSettings.json b/build/Properties/launchSettings.json deleted file mode 100644 index d175ae754..000000000 --- a/build/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "build": { - "commandName": "Project", - "commandLineArgs": "reset" - } - } -} \ No newline at end of file diff --git a/build/build.cmd b/build/build.cmd deleted file mode 100644 index 14fc0bdaa..000000000 --- a/build/build.cmd +++ /dev/null @@ -1,18 +0,0 @@ -@echo Off -set buildRoot="%cd%" - -ECHO Restoring packages -dotnet restore - -ECHO Updating version numbers and generating build script -cd %~dp0 -dotnet run -- update -cd %buildRoot% - -ECHO Building package -call %~dp0build-inner.cmd - -ECHO Reset version numbers -cd %~dp0 -dotnet run -- reset -cd %buildRoot% \ No newline at end of file diff --git a/build/build.csproj b/build/build.csproj deleted file mode 100644 index dc431284e..000000000 --- a/build/build.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - netcoreapp1.1 - portable - true - build - Exe - build - ..\ImageSharp.ruleset - - - - - - - \ No newline at end of file diff --git a/build/reset-versions.cmd b/build/reset-versions.cmd deleted file mode 100644 index 31d1d2431..000000000 --- a/build/reset-versions.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo Off - -set buildRoot="%cd%" -cd %~dp0 - -dotnet run -- reset - -cd %buildRoot% \ No newline at end of file diff --git a/gitversion.yml b/gitversion.yml new file mode 100644 index 000000000..9fae0d8c2 --- /dev/null +++ b/gitversion.yml @@ -0,0 +1,31 @@ +# to create a new package you create a new release/tag +# in github appveyor will build it without the -cixxx tag +# it will then be deployable cleanly to nuget.org + +branches: + master: + tag: ci + mode: ContinuousDeployment + increment: Minor + prevent-increment-of-merged-branch-version: false + track-merge-target: true + pull-request: + regex: (pull|pull\-requests|pr)[/-] + mode: ContinuousDelivery + tag: PullRequest + increment: Inherit + prevent-increment-of-merged-branch-version: false + tag-number-pattern: '[/-](?\d+)[-/]' + track-merge-target: false + tracks-release-branches: false + is-release-branch: false + otherbranches: + regex: '.*' + mode: ContinuousDeployment + tag: ci + increment: Patch + prevent-increment-of-merged-branch-version: false + track-merge-target: true + is-release-branch: false +ignore: + sha: [] \ No newline at end of file diff --git a/packages.xml b/packages.xml new file mode 100644 index 000000000..c7ff4de4c --- /dev/null +++ b/packages.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/AvatarWithRoundedCorner/Program.cs b/samples/AvatarWithRoundedCorner/Program.cs index b164c8d3b..087bbc29d 100644 --- a/samples/AvatarWithRoundedCorner/Program.cs +++ b/samples/AvatarWithRoundedCorner/Program.cs @@ -1,69 +1,109 @@ - +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using SixLabors.Shapes; namespace AvatarWithRoundedCorner { - using System; - using System.Numerics; - using ImageSharp; - using SixLabors.Primitives; - using SixLabors.Shapes; - - class Program + static class Program { static void Main(string[] args) { System.IO.Directory.CreateDirectory("output"); + using (var img = Image.Load("fb.jpg")) + { + // as generate returns a new IImage make sure we dispose of it + using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 20))) + { + destRound.Save("output/fb.png"); + } - GenerateAvatar("fb.jpg", "output/fb.png", new Size(200, 200), 20); - GenerateAvatar("fb.jpg", "output/fb-round.png", new Size(200, 200), 100); - GenerateAvatar("fb.jpg", "output/fb-rounder.png", new Size(200, 200), 150); + using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 100))) + { + destRound.Save("output/fb-round.png"); + } + + using (Image destRound = img.Clone(x => x.ConvertToAvatar(new Size(200, 200), 150))) + { + destRound.Save("output/fb-rounder.png"); + } + + using (Image destRound = img.CloneAndConvertToAvatarWithoutApply(new Size(200, 200), 150)) + { + destRound.Save("output/fb-rounder-without-apply.png"); + } + + // the original `img` object has not been altered at all. + } } - private static void GenerateAvatar(string source, string destination, Size size, float cornerRadius) + // 1. The short way: + // Implements a full image mutating pipeline operating on IImageProcessingContext + // We need the dimensions of the resized image to deduce 'IPathCollection' needed to build the corners, + // so we implement an "inline" image processor by utilizing 'ImageExtensions.Apply()' + private static IImageProcessingContext ConvertToAvatar(this IImageProcessingContext processingContext, Size size, float cornerRadius) { - using (var image = Image.Load(source)) + return processingContext.Resize(new ResizeOptions { - image.Resize(new ImageSharp.Processing.ResizeOptions - { - Size = size, - Mode = ImageSharp.Processing.ResizeMode.Crop - }); + Size = size, + Mode = ResizeMode.Crop + }).Apply(i => ApplyRoundedCorners(i, cornerRadius)); + } - ApplyRoundedCourners(image, cornerRadius); - image.Save(destination); - } + // 2. A more verbose way, avoiding 'Apply()': + // First we create a resized clone of the image, then we draw the corners on that instance with Mutate(). + private static Image CloneAndConvertToAvatarWithoutApply(this Image image, Size size, float cornerRadius) + { + Image result = image.Clone( + ctx => ctx.Resize( + new ResizeOptions + { + Size = size, + Mode = ResizeMode.Crop + })); + + ApplyRoundedCorners(result, cornerRadius); + return result; } - public static void ApplyRoundedCourners(Image img, float cornerRadius) + // This method can be seen as an inline implementation of an `IImageProcessor`: + // (The combination of `IImageOperations.Apply()` + this could be replaced with an `IImageProcessor`) + public static void ApplyRoundedCorners(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) + IPathCollection corners = BuildCorners(img.Width, img.Height, cornerRadius); + + // mutating in here as we already have a cloned original + img.Mutate(x => x.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 - }); + BlenderMode = 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) + public static IPathCollection BuildCorners(int imageWidth, int imageHeight, float cornerRadius) { // first create a square - var rect = new SixLabors.Shapes.RectangularePolygon(-0.5f, -0.5f, cornerRadius, cornerRadius); + var rect = new RectangularePolygon(-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.EllipsePolygon(cornerRadius-0.5f, cornerRadius - 0.5f, cornerRadius)); + IPath cornerToptLeft = rect.Clip(new EllipsePolygon(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; + //lets make 3 more positioned correctly, we can do that by translating the orgional artound the center of the image + var center = new Vector2(imageWidth / 2F, imageHeight / 2F); - float rightPos = imageWidth - cornerToptLeft.Bounds.Width +1; + 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); + IPath cornerTopRight = cornerToptLeft.RotateDegree(90).Translate(rightPos, 0); + IPath cornerBottomLeft = cornerToptLeft.RotateDegree(-90).Translate(0, bottomPos); + IPath cornerBottomRight = cornerToptLeft.RotateDegree(180).Translate(rightPos, bottomPos); return new PathCollection(cornerToptLeft, cornerBottomLeft, cornerTopRight, cornerBottomRight); } diff --git a/samples/ChangeDefaultEncoderOptions/Program.cs b/samples/ChangeDefaultEncoderOptions/Program.cs index dab8d445c..a8fbd7599 100644 --- a/samples/ChangeDefaultEncoderOptions/Program.cs +++ b/samples/ChangeDefaultEncoderOptions/Program.cs @@ -1,6 +1,9 @@ -using System; -using ImageSharp; -using ImageSharp.Formats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg; namespace ChangeDefaultEncoderOptions { @@ -10,7 +13,7 @@ namespace ChangeDefaultEncoderOptions { // lets switch out the default encoder for jpeg to one // that saves at 90 quality and ignores the matadata - Configuration.Default.SetEncoder(ImageFormats.Jpeg, new ImageSharp.Formats.JpegEncoder() + Configuration.Default.SetEncoder(ImageFormats.Jpeg, new JpegEncoder() { Quality = 90, IgnoreMetadata = true diff --git a/src/ImageSharp.Drawing/Brushes/Brushes.cs b/src/ImageSharp.Drawing/Brushes/Brushes.cs index e39f3dd49..47e207e8c 100644 --- a/src/ImageSharp.Drawing/Brushes/Brushes.cs +++ b/src/ImageSharp.Drawing/Brushes/Brushes.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// A collection of methods for creating generic brushes. /// diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs index 33b516e5c..bb907281b 100644 --- a/src/ImageSharp.Drawing/Brushes/IBrush.cs +++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Brush represents a logical configuration of a brush which can be used to source pixel colors /// @@ -33,6 +32,6 @@ namespace ImageSharp.Drawing /// The when being applied to things like shapes would usually be the /// bounding box of the shape not necessarily the bounds of the whole image /// - BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options); + BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index 57fcd8ffa..4cd3585ec 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of an image brush for painting images within areas. /// @@ -22,19 +21,19 @@ namespace ImageSharp.Drawing.Brushes /// /// The image to paint. /// - private readonly ImageBase image; + private readonly ImageFrame image; /// /// Initializes a new instance of the class. /// /// The image. - public ImageBrush(ImageBase image) + public ImageBrush(ImageFrame image) { this.image = image; } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new ImageBrushApplicator(source, this.image, region, options); } @@ -47,7 +46,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The source image. /// - private readonly ImageBase source; + private readonly ImageFrame source; /// /// The y-length. @@ -76,7 +75,7 @@ namespace ImageSharp.Drawing.Brushes /// The image. /// The region. /// The options - public ImageBrushApplicator(ImageBase target, ImageBase image, RectangleF region, GraphicsOptions options) + public ImageBrushApplicator(ImageFrame target, ImageFrame image, RectangleF region, GraphicsOptions options) : base(target, options) { this.source = image; @@ -119,7 +118,7 @@ namespace ImageSharp.Drawing.Brushes { int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; - Span sourceRow = this.source.GetRowSpan(sourceY); + Span sourceRow = this.source.GetPixelRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { @@ -130,7 +129,7 @@ namespace ImageSharp.Drawing.Brushes overlay[i] = pixel; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index 5fcb92bdc..844df0e0e 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of a pattern brush for painting patterns. /// @@ -93,7 +92,7 @@ namespace ImageSharp.Drawing.Brushes } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new PatternBrushApplicator(source, this.pattern, this.patternVector, options); } @@ -116,7 +115,7 @@ namespace ImageSharp.Drawing.Brushes /// The pattern. /// The patternVector. /// The options - public PatternBrushApplicator(ImageBase source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) + public PatternBrushApplicator(ImageFrame source, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) : base(source, options) { this.pattern = pattern; @@ -164,7 +163,7 @@ namespace ImageSharp.Drawing.Brushes overlay[i] = this.pattern[patternY, patternX]; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 29c625d7f..ca6f7630d 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Brushes.Processors +{ /// /// primitive that converts a point in to a color for discovering the fill color based on an implementation /// @@ -23,7 +21,7 @@ namespace ImageSharp.Drawing.Processors /// /// The target. /// The options. - internal BrushApplicator(ImageBase target, GraphicsOptions options) + internal BrushApplicator(ImageFrame target, GraphicsOptions options) { this.Target = target; @@ -40,7 +38,7 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the destinaion /// - protected ImageBase Target { get; } + protected ImageFrame Target { get; } /// /// Gets the blend percentage @@ -80,7 +78,7 @@ namespace ImageSharp.Drawing.Processors overlay[i] = this[x + i, y]; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index 27aa99b97..ba2fca4e4 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of a brush that can recolor an image /// @@ -58,7 +57,7 @@ namespace ImageSharp.Drawing.Brushes public TPixel TargeTPixel { get; } /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new RecolorBrushApplicator(source, this.SourceColor, this.TargeTPixel, this.Threshold, options); } @@ -93,7 +92,7 @@ namespace ImageSharp.Drawing.Brushes /// Color of the target. /// The threshold . /// The options - public RecolorBrushApplicator(ImageBase source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) + public RecolorBrushApplicator(ImageFrame source, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) : base(source, options) { this.sourceColor = sourceColor.ToVector4(); @@ -159,7 +158,7 @@ namespace ImageSharp.Drawing.Brushes overlay[i] = this[offsetX, y]; } - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs index 21ebb88a0..658164339 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Processors; - using SixLabors.Primitives; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Brushes +{ /// /// Provides an implementation of a solid brush for painting solid color areas. /// @@ -43,7 +42,7 @@ namespace ImageSharp.Drawing.Brushes public TPixel Color => this.color; /// - public BrushApplicator CreateApplicator(ImageBase source, RectangleF region, GraphicsOptions options) + public BrushApplicator CreateApplicator(ImageFrame source, RectangleF region, GraphicsOptions options) { return new SolidBrushApplicator(source, this.color, options); } @@ -59,7 +58,7 @@ namespace ImageSharp.Drawing.Brushes /// The source image. /// The color. /// The options - public SolidBrushApplicator(ImageBase source, TPixel color, GraphicsOptions options) + public SolidBrushApplicator(ImageFrame source, TPixel color, GraphicsOptions options) : base(source, options) { this.Colors = new Buffer(source.Width); @@ -93,16 +92,23 @@ namespace ImageSharp.Drawing.Brushes /// internal override void Apply(Span scanline, int x, int y) { - Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); - - using (var amountBuffer = new Buffer(scanline.Length)) + try { - for (int i = 0; i < scanline.Length; i++) + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); + + using (var amountBuffer = new Buffer(scanline.Length)) { - amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; - } + for (int i = 0; i < scanline.Length; i++) + { + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + } - this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer); + this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer); + } + } + catch (Exception) + { + throw; } } } diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 03eb7be28..d55e22416 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing.Processors; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The location to draw the blended image. /// The options. /// The . - public static Image DrawImage(this Image source, Image image, Size size, Point location, GraphicsOptions options) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, Size size, Point location, GraphicsOptions options) where TPixel : struct, IPixel { if (size == default(Size)) @@ -37,7 +35,7 @@ namespace ImageSharp location = Point.Empty; } - source.ApplyProcessor(new DrawImageProcessor(image, size, location, options), source.Bounds); + source.ApplyProcessor(new DrawImageProcessor(image, size, location, options)); return source; } @@ -49,7 +47,7 @@ namespace ImageSharp /// The image to blend with the currently processing image. /// The opacity of the image image to blend. Must be between 0 and 1. /// The . - public static Image Blend(this Image source, Image image, float percent) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, float percent) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; @@ -66,7 +64,7 @@ namespace ImageSharp /// The blending mode. /// The opacity of the image image to blend. Must be between 0 and 1. /// The . - public static Image Blend(this Image source, Image image, PixelBlenderMode blender, float percent) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; @@ -83,7 +81,7 @@ namespace ImageSharp /// The image to blend with the currently processing image. /// The options, including the blending type and belnding amount. /// The . - public static Image Blend(this Image source, Image image, GraphicsOptions options) + public static IImageProcessingContext Blend(this IImageProcessingContext source, Image image, GraphicsOptions options) where TPixel : struct, IPixel { return DrawImage(source, image, default(Size), default(Point), options); @@ -99,7 +97,7 @@ namespace ImageSharp /// The size to draw the blended image. /// The location to draw the blended image. /// The . - public static Image DrawImage(this Image source, Image image, float percent, Size size, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, float percent, Size size, Point location) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; @@ -118,7 +116,7 @@ namespace ImageSharp /// The size to draw the blended image. /// The location to draw the blended image. /// The . - public static Image DrawImage(this Image source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) + public static IImageProcessingContext DrawImage(this IImageProcessingContext source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) where TPixel : struct, IPixel { GraphicsOptions options = GraphicsOptions.Default; diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index b3ee2ed99..2e5d311c6 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Processors; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,10 +21,10 @@ namespace ImageSharp /// The details how to fill the region of interest. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Apply(new FillProcessor(brush, options)); + return source.ApplyProcessor(new FillProcessor(brush, options)); } /// @@ -36,7 +34,7 @@ namespace ImageSharp /// The image this method extends. /// The details how to fill the region of interest. /// The . - public static Image Fill(this Image source, IBrush brush) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush) where TPixel : struct, IPixel { return source.Fill(brush, GraphicsOptions.Default); @@ -49,7 +47,7 @@ namespace ImageSharp /// The image this method extends. /// The color. /// The . - public static Image Fill(this Image source, TPixel color) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color)); @@ -64,10 +62,10 @@ namespace ImageSharp /// The region. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Apply(new FillRegionProcessor(brush, region, options)); + return source.ApplyProcessor(new FillRegionProcessor(brush, region, options)); } /// @@ -78,7 +76,7 @@ namespace ImageSharp /// The brush. /// The region. /// The . - public static Image Fill(this Image source, IBrush brush, Region region) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Region region) where TPixel : struct, IPixel { return source.Fill(brush, region, GraphicsOptions.Default); @@ -93,7 +91,7 @@ namespace ImageSharp /// The region. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, Region region, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Region region, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), region, options); @@ -107,7 +105,7 @@ namespace ImageSharp /// The color. /// The region. /// The . - public static Image Fill(this Image source, TPixel color, Region region) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Region region) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), region); diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index ddf18e428..5918e7705 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -1,20 +1,21 @@  An extension to ImageSharp that allows the drawing of images, paths, and text. - ImageSharp.Drawing - 1.0.0-alpha9 - James Jackson-South and contributors + SixLabors.ImageSharp.Drawing + $(packageversion) + 0.0.1 + Six Labor and contributors netstandard1.1 true true - ImageSharp.Drawing - ImageSharp.Drawing + SixLabors.ImageSharp.Drawing + SixLabors.ImageSharp.Drawing Image Draw Shape Path Font - https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-128.png - https://github.com/JimBobSquarePants/ImageSharp + https://raw.githubusercontent.com/SixLabor/ImageSharp/master/build/icons/imagesharp-logo-128.png + https://github.com/SixLabors/ImageSharp http://www.apache.org/licenses/LICENSE-2.0 git - https://github.com/JimBobSquarePants/ImageSharp + https://github.com/SixLabor/ImageSharp false false false @@ -30,19 +31,22 @@ - + - + + + All ..\..\ImageSharp.ruleset + SixLabors.ImageSharp.Drawing true diff --git a/src/ImageSharp.Drawing/Paths/DrawBeziers.cs b/src/ImageSharp.Drawing/Paths/DrawBeziers.cs index 59bcf4036..de4fdd003 100644 --- a/src/ImageSharp.Drawing/Paths/DrawBeziers.cs +++ b/src/ImageSharp.Drawing/Paths/DrawBeziers.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points)), options); @@ -43,7 +41,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, PointF[] points) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new CubicBezierLineSegment(points))); @@ -58,7 +56,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawBeziers(this Image source, TPixel color, float thickness, PointF[] points) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.DrawBeziers(new SolidBrush(color), thickness, points); @@ -74,7 +72,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawBeziers(this Image source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.DrawBeziers(new SolidBrush(color), thickness, points, options); @@ -89,7 +87,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawBeziers(this Image source, IPen pen, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new CubicBezierLineSegment(points)), options); @@ -103,7 +101,7 @@ namespace ImageSharp /// The pen. /// The points. /// The . - public static Image DrawBeziers(this Image source, IPen pen, PointF[] points) + public static IImageProcessingContext DrawBeziers(this IImageProcessingContext source, IPen pen, PointF[] points) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new CubicBezierLineSegment(points))); diff --git a/src/ImageSharp.Drawing/Paths/DrawLines.cs b/src/ImageSharp.Drawing/Paths/DrawLines.cs index 3ce0dc4da..e5d9a1b3b 100644 --- a/src/ImageSharp.Drawing/Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing/Paths/DrawLines.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawLines(this Image source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); @@ -43,7 +41,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawLines(this Image source, IBrush brush, float thickness, PointF[] points) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); @@ -58,7 +56,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawLines(this Image source, TPixel color, float thickness, PointF[] points) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.DrawLines(new SolidBrush(color), thickness, points); @@ -74,7 +72,7 @@ namespace ImageSharp /// The points. /// The options. /// The .> - public static Image DrawLines(this Image source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.DrawLines(new SolidBrush(color), thickness, points, options); @@ -89,7 +87,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawLines(this Image source, IPen pen, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IPen pen, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points)), options); @@ -103,7 +101,7 @@ namespace ImageSharp /// The pen. /// The points. /// The . - public static Image DrawLines(this Image source, IPen pen, PointF[] points) + public static IImageProcessingContext DrawLines(this IImageProcessingContext source, IPen pen, PointF[] points) where TPixel : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points))); diff --git a/src/ImageSharp.Drawing/Paths/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPath.cs index 1fba06370..b6c821a60 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPath.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(pen.StrokeFill, new ShapePath(path, pen), options); @@ -39,7 +37,7 @@ namespace ImageSharp /// The pen. /// The path. /// The . - public static Image Draw(this Image source, IPen pen, IPath path) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPath path) where TPixel : struct, IPixel { return source.Draw(pen, path, GraphicsOptions.Default); @@ -55,7 +53,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), path, options); @@ -70,7 +68,7 @@ namespace ImageSharp /// The thickness. /// The path. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPath path) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), path); @@ -86,7 +84,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPath path, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path, options); @@ -101,7 +99,7 @@ namespace ImageSharp /// The thickness. /// The path. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPath path) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPath path) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path); diff --git a/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs index 877737653..a126663b0 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The paths. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { foreach (IPath path in paths) @@ -44,7 +42,7 @@ namespace ImageSharp /// The pen. /// The paths. /// The . - public static Image Draw(this Image source, IPen pen, IPathCollection paths) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, IPathCollection paths) where TPixel : struct, IPixel { return source.Draw(pen, paths, GraphicsOptions.Default); @@ -60,7 +58,7 @@ namespace ImageSharp /// The shapes. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), paths, options); @@ -75,7 +73,7 @@ namespace ImageSharp /// The thickness. /// The paths. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPathCollection paths) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, IPathCollection paths) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), paths); @@ -91,7 +89,7 @@ namespace ImageSharp /// The paths. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, paths, options); @@ -106,7 +104,7 @@ namespace ImageSharp /// The thickness. /// The paths. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, IPathCollection paths) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, IPathCollection paths) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, paths); diff --git a/src/ImageSharp.Drawing/Paths/DrawPolygon.cs b/src/ImageSharp.Drawing/Paths/DrawPolygon.cs index 4fa469a49..771ea9e61 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPolygon.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); @@ -43,7 +41,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, PointF[] points) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IBrush brush, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); @@ -58,7 +56,7 @@ namespace ImageSharp /// The thickness. /// The points. /// The . - public static Image DrawPolygon(this Image source, TPixel color, float thickness, PointF[] points) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points) where TPixel : struct, IPixel { return source.DrawPolygon(new SolidBrush(color), thickness, points); @@ -74,7 +72,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawPolygon(this Image source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, TPixel color, float thickness, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.DrawPolygon(new SolidBrush(color), thickness, points, options); @@ -88,7 +86,7 @@ namespace ImageSharp /// The pen. /// The points. /// The . - public static Image DrawPolygon(this Image source, IPen pen, PointF[] points) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IPen pen, PointF[] points) where TPixel : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default); @@ -103,7 +101,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image DrawPolygon(this Image source, IPen pen, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext DrawPolygon(this IImageProcessingContext source, IPen pen, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options); diff --git a/src/ImageSharp.Drawing/Paths/DrawRectangle.cs b/src/ImageSharp.Drawing/Paths/DrawRectangle.cs index b3f0e6fc3..6b98d1f8e 100644 --- a/src/ImageSharp.Drawing/Paths/DrawRectangle.cs +++ b/src/ImageSharp.Drawing/Paths/DrawRectangle.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(pen, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height), options); @@ -39,7 +37,7 @@ namespace ImageSharp /// The pen. /// The shape. /// The . - public static Image Draw(this Image source, IPen pen, RectangleF shape) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IPen pen, RectangleF shape) where TPixel : struct, IPixel { return source.Draw(pen, shape, GraphicsOptions.Default); @@ -55,7 +53,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), shape, options); @@ -70,7 +68,7 @@ namespace ImageSharp /// The thickness. /// The shape. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, RectangleF shape) + public static IImageProcessingContext Draw(this IImageProcessingContext source, IBrush brush, float thickness, RectangleF shape) where TPixel : struct, IPixel { return source.Draw(new Pen(brush, thickness), shape); @@ -86,7 +84,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, shape, options); @@ -101,7 +99,7 @@ namespace ImageSharp /// The thickness. /// The shape. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, RectangleF shape) + public static IImageProcessingContext Draw(this IImageProcessingContext source, TPixel color, float thickness, RectangleF shape) where TPixel : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, shape); diff --git a/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs b/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs index abb5ef73a..fff082f2d 100644 --- a/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs +++ b/src/ImageSharp.Drawing/Paths/FillPathBuilder.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using System; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,7 +23,7 @@ namespace ImageSharp /// The shape. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, Action path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Action path, GraphicsOptions options) where TPixel : struct, IPixel { var pb = new PathBuilder(); @@ -42,7 +40,7 @@ namespace ImageSharp /// The brush. /// The path. /// The . - public static Image Fill(this Image source, IBrush brush, Action path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, Action path) where TPixel : struct, IPixel { return source.Fill(brush, path, GraphicsOptions.Default); @@ -57,7 +55,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, Action path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Action path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path, options); @@ -71,7 +69,7 @@ namespace ImageSharp /// The color. /// The path. /// The . - public static Image Fill(this Image source, TPixel color, Action path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, Action path) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path); diff --git a/src/ImageSharp.Drawing/Paths/FillPathCollection.cs b/src/ImageSharp.Drawing/Paths/FillPathCollection.cs index 3ea9fb94b..b252b95d5 100644 --- a/src/ImageSharp.Drawing/Paths/FillPathCollection.cs +++ b/src/ImageSharp.Drawing/Paths/FillPathCollection.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The shapes. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { foreach (IPath s in paths) @@ -43,7 +41,7 @@ namespace ImageSharp /// The brush. /// The paths. /// The . - public static Image Fill(this Image source, IBrush brush, IPathCollection paths) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPathCollection paths) where TPixel : struct, IPixel { return source.Fill(brush, paths, GraphicsOptions.Default); @@ -58,7 +56,7 @@ namespace ImageSharp /// The paths. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, IPathCollection paths, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), paths, options); @@ -72,7 +70,7 @@ namespace ImageSharp /// The color. /// The paths. /// The . - public static Image Fill(this Image source, TPixel color, IPathCollection paths) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPathCollection paths) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), paths); diff --git a/src/ImageSharp.Drawing/Paths/FillPaths.cs b/src/ImageSharp.Drawing/Paths/FillPaths.cs index f579c4ad0..f554ed758 100644 --- a/src/ImageSharp.Drawing/Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing/Paths/FillPaths.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Shapes; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The shape. /// The graphics options. /// The . - public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), options); @@ -38,7 +36,7 @@ namespace ImageSharp /// The brush. /// The path. /// The . - public static Image Fill(this Image source, IBrush brush, IPath path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, IPath path) where TPixel : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); @@ -53,7 +51,7 @@ namespace ImageSharp /// The path. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, IPath path, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPath path, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path, options); @@ -67,7 +65,7 @@ namespace ImageSharp /// The color. /// The path. /// The . - public static Image Fill(this Image source, TPixel color, IPath path) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, IPath path) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), path); diff --git a/src/ImageSharp.Drawing/Paths/FillPolygon.cs b/src/ImageSharp.Drawing/Paths/FillPolygon.cs index 6266d3bd6..d8723bc31 100644 --- a/src/ImageSharp.Drawing/Paths/FillPolygon.cs +++ b/src/ImageSharp.Drawing/Paths/FillPolygon.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -27,7 +25,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image FillPolygon(this Image source, IBrush brush, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, IBrush brush, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); @@ -41,7 +39,7 @@ namespace ImageSharp /// The brush. /// The points. /// The . - public static Image FillPolygon(this Image source, IBrush brush, PointF[] points) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, IBrush brush, PointF[] points) where TPixel : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points))); @@ -56,7 +54,7 @@ namespace ImageSharp /// The points. /// The options. /// The . - public static Image FillPolygon(this Image source, TPixel color, PointF[] points, GraphicsOptions options) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, TPixel color, PointF[] points, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); @@ -70,7 +68,7 @@ namespace ImageSharp /// The color. /// The points. /// The . - public static Image FillPolygon(this Image source, TPixel color, PointF[] points) + public static IImageProcessingContext FillPolygon(this IImageProcessingContext source, TPixel color, PointF[] points) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); diff --git a/src/ImageSharp.Drawing/Paths/FillRectangle.cs b/src/ImageSharp.Drawing/Paths/FillRectangle.cs index bd6460cf9..52578de17 100644 --- a/src/ImageSharp.Drawing/Paths/FillRectangle.cs +++ b/src/ImageSharp.Drawing/Paths/FillRectangle.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using Drawing; - using Drawing.Brushes; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Fill(this Image source, IBrush brush, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height), options); @@ -38,7 +36,7 @@ namespace ImageSharp /// The brush. /// The shape. /// The . - public static Image Fill(this Image source, IBrush brush, RectangleF shape) + public static IImageProcessingContext Fill(this IImageProcessingContext source, IBrush brush, RectangleF shape) where TPixel : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.RectangularePolygon(shape.X, shape.Y, shape.Width, shape.Height)); @@ -53,7 +51,7 @@ namespace ImageSharp /// The shape. /// The options. /// The . - public static Image Fill(this Image source, TPixel color, RectangleF shape, GraphicsOptions options) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, RectangleF shape, GraphicsOptions options) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), shape, options); @@ -67,7 +65,7 @@ namespace ImageSharp /// The color. /// The shape. /// The . - public static Image Fill(this Image source, TPixel color, RectangleF shape) + public static IImageProcessingContext Fill(this IImageProcessingContext source, TPixel color, RectangleF shape) where TPixel : struct, IPixel { return source.Fill(new SolidBrush(color), shape); diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs index ec4f222e0..61f1291c4 100644 --- a/src/ImageSharp.Drawing/Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using System; - using System.Buffers; - using System.Numerics; - - using SixLabors.Shapes; +using System; +using System.Buffers; +using System.Numerics; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp.Drawing +{ /// /// A mapping between a and a region. /// diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index a7a72c4f3..a96b03dd0 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using System; - using System.Buffers; - using System.Numerics; - using ImageSharp.Memory; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System; +using System.Buffers; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp.Drawing +{ /// /// A mapping between a and a region. /// @@ -44,18 +42,18 @@ namespace ImageSharp.Drawing public override Rectangle Bounds { get; } /// - public override int Scan(float y, Span buffer) + public override int Scan(float y, float[] buffer, int offset) { var start = new PointF(this.Bounds.Left - 1, y); var end = new PointF(this.Bounds.Right + 1, y); using (var innerBuffer = new Buffer(buffer.Length)) { - var span = innerBuffer.Span; - int count = this.Shape.FindIntersections(start, end, span); + PointF[] array = innerBuffer.Array; + int count = this.Shape.FindIntersections(start, end, array, 0); for (int i = 0; i < count; i++) { - buffer[i] = span[i].X; + buffer[i + offset] = array[i].X; } return count; diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs index 20ac53daf..0680ec2f5 100644 --- a/src/ImageSharp.Drawing/Pens/IPen.cs +++ b/src/ImageSharp.Drawing/Pens/IPen.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Pens -{ - using ImageSharp.PixelFormats; - using Processors; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Pens +{ /// /// Interface representing a Pen /// diff --git a/src/ImageSharp.Drawing/Pens/Pens.cs b/src/ImageSharp.Drawing/Pens/Pens.cs index 364115cb7..6478b1b81 100644 --- a/src/ImageSharp.Drawing/Pens/Pens.cs +++ b/src/ImageSharp.Drawing/Pens/Pens.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Pens -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Pens +{ /// /// Common Pen styles /// diff --git a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs index 37cfff9e9..74e4c6596 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Pens -{ - using System; - using System.Numerics; - - using ImageSharp.Drawing.Brushes; - using ImageSharp.PixelFormats; - using Processors; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing.Pens +{ /// /// Provides a pen that can apply a pattern to a line with a set brush and thickness /// diff --git a/src/ImageSharp.Drawing/PointInfo.cs b/src/ImageSharp.Drawing/PointInfo.cs deleted file mode 100644 index 7eff24fac..000000000 --- a/src/ImageSharp.Drawing/PointInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - /// - /// Returns details about how far away from the inside of a shape and the color the pixel could be. - /// - public struct PointInfo - { - /// - /// The distance along path - /// - public float DistanceAlongPath; - - /// - /// The distance from path - /// - public float DistanceFromPath; - } -} diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 62344b101..213ab1b4a 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -1,19 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Processors +{ /// /// Combines two images together by blending the pixels. /// @@ -61,20 +60,20 @@ namespace ImageSharp.Drawing.Processors public Point Location { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Image disposableImage = null; Image targetImage = this.Image; try { - if (targetImage.Bounds.Size != this.Size) + if (targetImage.Size() != this.Size) { - targetImage = disposableImage = new Image(this.Image).Resize(this.Size.Width, this.Size.Height); + targetImage = disposableImage = this.Image.Clone(x => x.Resize(this.Size.Width, this.Size.Height)); } // Align start/end positions. - Rectangle bounds = this.Image.Bounds; + Rectangle bounds = this.Image.Bounds(); int minX = Math.Max(this.Location.X, sourceRectangle.X); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); maxX = Math.Min(this.Location.X + this.Size.Width, maxX); @@ -97,7 +96,7 @@ namespace ImageSharp.Drawing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { Span background = sourcePixels.GetRowSpan(y).Slice(minX, width); diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 6eb12cf48..679ca6a22 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -1,21 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using Drawing; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing.Processors +{ /// /// Using the bursh as a source of pixels colors blends the brush color with source. /// @@ -41,7 +40,7 @@ namespace ImageSharp.Drawing.Processors } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; @@ -67,9 +66,6 @@ namespace ImageSharp.Drawing.Processors int width = maxX - minX; - // We could possibly do some optimization by having knowledge about the individual brushes operate - // for example If brush is SolidBrush then we could just get the color upfront - // and skip using the IBrushApplicator?. using (var amount = new Buffer(width)) using (BrushApplicator applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options)) { @@ -81,7 +77,7 @@ namespace ImageSharp.Drawing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index c39882432..d867008d7 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -1,20 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Processors +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Brushes.Processors; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Drawing.Processors { - using System; - using System.Buffers; - using System.Runtime.CompilerServices; - using Drawing; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - /// /// Usinf a brsuh and a shape fills shape with contents of brush the /// @@ -58,7 +58,7 @@ namespace ImageSharp.Drawing.Processors public GraphicsOptions Options { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Region region = this.Region; Rectangle rect = region.Bounds; @@ -94,7 +94,6 @@ namespace ImageSharp.Drawing.Processors using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { float[] buffer = arrayPool.Rent(maxIntersections); - Span bufferSpan = buffer.AsSpan().Slice(0, maxIntersections); int scanlineWidth = maxX - minX; using (var scanline = new Buffer(scanlineWidth)) { @@ -118,14 +117,14 @@ namespace ImageSharp.Drawing.Processors float subpixelFractionPoint = subpixelFraction / subpixelCount; for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) { - int pointsFound = region.Scan(subPixel, bufferSpan); + int pointsFound = region.Scan(subPixel, buffer, 0); if (pointsFound == 0) { // nothing on this line skip continue; } - QuickSort(bufferSpan.Slice(0, pointsFound)); + QuickSort(new Span(buffer, 0, pointsFound)); for (int point = 0; point < pointsFound; point += 2) { diff --git a/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs b/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs index fba25a9db..2891598b9 100644 --- a/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs +++ b/src/ImageSharp.Drawing/Properties/AssemblyInfo.cs @@ -1,6 +1,4 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// // Common values read from `AssemblyInfo.Common.cs` diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs index 23028b9a8..c5e7c1cfd 100644 --- a/src/ImageSharp.Drawing/Region.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using System; - using SixLabors.Primitives; +using System; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Drawing +{ /// /// Represents a region of an image. /// @@ -22,7 +20,7 @@ namespace ImageSharp.Drawing /// Gets the bounding box that entirely surrounds this region. /// /// - /// This should always contains all possible points returned from . + /// This should always contains all possible points returned from . /// public abstract Rectangle Bounds { get; } @@ -31,7 +29,8 @@ namespace ImageSharp.Drawing /// /// The position along the y axis to find intersections. /// The buffer. + /// The point in the buffer to start setting offset. /// The number of intersections found. - public abstract int Scan(float y, Span buffer); + public abstract int Scan(float y, float[] buffer, int offset); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Text/DrawText.Path.cs b/src/ImageSharp.Drawing/Text/DrawText.Path.cs index 523813188..274b59205 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.Path.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.Path.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Fonts; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.Fonts; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -31,7 +28,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, color, path, TextGraphicsOptions.Default); @@ -50,7 +47,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, Brushes.Solid(color), null, path, options); @@ -68,7 +65,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, path, TextGraphicsOptions.Default); @@ -87,7 +84,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, null, path, options); @@ -105,7 +102,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, pen, path, TextGraphicsOptions.Default); @@ -124,7 +121,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, null, pen, path, options); @@ -143,7 +140,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, IPath path) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, pen, path, TextGraphicsOptions.Default); @@ -163,16 +160,11 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, IPath path, TextGraphicsOptions options) where TPixel : struct, IPixel { float dpiX = DefaultTextDpi; float dpiY = DefaultTextDpi; - if (options.UseImageResolution) - { - dpiX = (float)source.MetaData.HorizontalResolution; - dpiY = (float)source.MetaData.VerticalResolution; - } var style = new RendererOptions(font, dpiX, dpiY) { diff --git a/src/ImageSharp.Drawing/Text/DrawText.cs b/src/ImageSharp.Drawing/Text/DrawText.cs index 6352836a9..c0105011e 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using ImageSharp.PixelFormats; - using SixLabors.Fonts; - using SixLabors.Primitives; - using SixLabors.Shapes; +using System.Numerics; +using SixLabors.Fonts; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SixLabors.Shapes; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -34,7 +31,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, color, location, TextGraphicsOptions.Default); @@ -53,7 +50,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, TPixel color, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, TPixel color, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, Brushes.Solid(color), null, location, options); @@ -71,7 +68,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default); @@ -90,7 +87,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, null, location, options); @@ -108,7 +105,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default); @@ -127,7 +124,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IPen pen, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { return source.DrawText(text, font, null, pen, location, options); @@ -146,7 +143,7 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, PointF location) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, PointF location) where TPixel : struct, IPixel { return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default); @@ -166,16 +163,11 @@ namespace ImageSharp /// /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, PointF location, TextGraphicsOptions options) + public static IImageProcessingContext DrawText(this IImageProcessingContext source, string text, Font font, IBrush brush, IPen pen, PointF location, TextGraphicsOptions options) where TPixel : struct, IPixel { float dpiX = DefaultTextDpi; float dpiY = DefaultTextDpi; - if (options.UseImageResolution) - { - dpiX = (float)source.MetaData.HorizontalResolution; - dpiY = (float)source.MetaData.VerticalResolution; - } var style = new RendererOptions(font, dpiX, dpiY, location) { diff --git a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs index 593ac36d4..c230a8c79 100644 --- a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing -{ - using ImageSharp.PixelFormats; - using SixLabors.Fonts; +using SixLabors.Fonts; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Drawing +{ /// /// Options for influencing the drawing functions. /// @@ -29,8 +27,6 @@ namespace ImageSharp.Drawing private PixelBlenderMode blenderMode; - private bool? useImageResolution; - private float wrapTextWidth; private SixLabors.Fonts.HorizontalAlignment? horizontalAlignment; @@ -44,7 +40,6 @@ namespace ImageSharp.Drawing { this.applyKerning = true; this.tabWidth = 4; - this.useImageResolution = false; this.wrapTextWidth = 0; this.horizontalAlignment = HorizontalAlignment.Left; this.verticalAlignment = VerticalAlignment.Top; @@ -89,12 +84,6 @@ namespace ImageSharp.Drawing /// public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } - /// - /// Gets or sets a value indicating whether to use the current image resultion to for point size scaling. - /// If this is [false] the text renders at 72dpi otherwise it renders at Image resolution - /// - public bool UseImageResolution { get => this.useImageResolution ?? false; set => this.useImageResolution = value; } - /// /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. /// diff --git a/src/ImageSharp/Advanced/IConfigurable.cs b/src/ImageSharp/Advanced/IConfigurable.cs new file mode 100644 index 000000000..fd97ae921 --- /dev/null +++ b/src/ImageSharp/Advanced/IConfigurable.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Encapsulates the properties for configuration + /// + internal interface IConfigurable + { + /// + /// Gets the configuration. + /// + Configuration Configuration { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Advanced/IPixelSource.cs b/src/ImageSharp/Advanced/IPixelSource.cs new file mode 100644 index 000000000..c9edf118c --- /dev/null +++ b/src/ImageSharp/Advanced/IPixelSource.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + /// The type of the pixel. + internal interface IPixelSource + where TPixel : struct, IPixel + { + /// + /// Gets the pixel buffer. + /// + Buffer2D PixelBuffer { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Advanced/ImageExtensions.cs b/src/ImageSharp/Advanced/ImageExtensions.cs new file mode 100644 index 000000000..f4043b5ad --- /dev/null +++ b/src/ImageSharp/Advanced/ImageExtensions.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Extension methods over Image{TPixel} + /// + internal static class ImageExtensions + { + /// + /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. + /// + /// The type of the pixel. + /// The source. + /// The + public static Span GetPixelSpan(this ImageFrame source) + where TPixel : struct, IPixel + => GetSpan(source); + + /// + /// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// The + public static Span GetPixelRowSpan(this ImageFrame source, int row) + where TPixel : struct, IPixel + => GetSpan(source, row); + + /// + /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. + /// + /// The type of the pixel. + /// The source. + /// The + public static Span GetPixelSpan(this Image source) + where TPixel : struct, IPixel + => source.Frames.RootFrame.GetPixelSpan(); + + /// + /// Gets the representation of the pixels as an area of contiguous memory at row 'y' beginning from the the first pixel on that row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// The + public static Span GetPixelRowSpan(this Image source, int row) + where TPixel : struct, IPixel + => source.Frames.RootFrame.GetPixelRowSpan(row); + + /// + /// Gets the configuration for the image. + /// + /// The Pixel format. + /// The source image + /// Returns the configuration. + public static Configuration GetConfiguration(this Image source) + where TPixel : struct, IPixel + => GetConfiguration((IConfigurable)source); + + /// + /// Gets the span to the backing buffer. + /// + /// The type of the pixel. + /// The source. + /// The span retuned from Pixel source + private static Span GetSpan(IPixelSource source) + where TPixel : struct, IPixel + => source.PixelBuffer.Span; + + /// + /// Gets the span to the backing buffer at the given row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// + /// The span retuned from Pixel source + /// + private static Span GetSpan(IPixelSource source, int row) + where TPixel : struct, IPixel + => GetSpan(source.PixelBuffer, row); + + /// + /// Gets the span to the backing buffer at the given row. + /// + /// The type of the pixel. + /// The source. + /// The row. + /// + /// The span retuned from Pixel source + /// + private static Span GetSpan(Buffer2D source, int row) + where TPixel : struct, IPixel + => source.Span.Slice(row * source.Width, source.Width); + + /// + /// Gets the configuration. + /// + /// The source image + /// Returns the bounds of the image + private static Configuration GetConfiguration(IConfigurable source) + => source?.Configuration ?? Configuration.Default; + } +} diff --git a/src/ImageSharp/ApplyProcessors.cs b/src/ImageSharp/ApplyProcessors.cs new file mode 100644 index 000000000..58a952c40 --- /dev/null +++ b/src/ImageSharp/ApplyProcessors.cs @@ -0,0 +1,104 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Mutates the source image by applying the image operation to it. + /// + /// The pixel format. + /// The image to mutate. + /// The operation to perform on the source. + public static void Mutate(this Image source, Action> operation) + where TPixel : struct, IPixel + { + Guard.NotNull(operation, nameof(operation)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); + operation(operationsRunner); + operationsRunner.Apply(); + } + + /// + /// Mutates the source image by applying the operations to it. + /// + /// The pixel format. + /// The image to mutate. + /// The operations to perform on the source. + public static void Mutate(this Image source, params IImageProcessor[] operations) + where TPixel : struct, IPixel + { + Guard.NotNull(operations, nameof(operations)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, true); + operationsRunner.ApplyProcessors(operations); + operationsRunner.Apply(); + } + + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operation. + /// + /// The pixel format. + /// The image to clone. + /// The operation to perform on the clone. + /// The new + public static Image Clone(this Image source, Action> operation) + where TPixel : struct, IPixel + { + Guard.NotNull(operation, nameof(operation)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); + operation(operationsRunner); + return operationsRunner.Apply(); + } + + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operations. + /// + /// The pixel format. + /// The image to clone. + /// The operations to perform on the clone. + /// The new + public static Image Clone(this Image source, params IImageProcessor[] operations) + where TPixel : struct, IPixel + { + Guard.NotNull(operations, nameof(operations)); + Guard.NotNull(source, nameof(source)); + + IInternalImageProcessingContext operationsRunner = source.GetConfiguration().ImageOperationsProvider.CreateImageProcessingContext(source, false); + operationsRunner.ApplyProcessors(operations); + return operationsRunner.Apply(); + } + + /// + /// Applies the given collection against the context + /// + /// The pixel format. + /// The image processing context. + /// The operations to perform on the source. + /// The to allow chaining of operations. + public static IImageProcessingContext ApplyProcessors(this IImageProcessingContext source, params IImageProcessor[] operations) + where TPixel : struct, IPixel + { + foreach (IImageProcessor p in operations) + { + source = source.ApplyProcessor(p); + } + + return source; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs index d382bbedb..107be4cb2 100644 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ b/src/ImageSharp/ColorSpaces/CieLab.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents a CIE L*a*b* 1976 color. /// diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 03cf80bf1..834ef56a8 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/src/ImageSharp/ColorSpaces/CieLch.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs index a4e8b424d..f35914d64 100644 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs index c3fd626e6..9b5251708 100644 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLuv.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International /// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index d9bfe88cd..d9767d45e 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents the coordinates of CIEXY chromaticity space /// diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs index 1578f1ac3..d5ef4b15d 100644 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/CieXyy.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an CIE xyY 1931 color /// diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index 8d3255e65..908408000 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/CieXyz.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an CIE XYZ 1931 color /// diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index eeaef21bd..2a58a5762 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs index 29200823e..2bcdc5127 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Constants use for Cie conversion calculations diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs index acdb356a2..80f9e6789 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using System; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Performs chromatic adaptation on the various color spaces. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 2c274c17a..9268f3a70 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index e3b3975a4..13dae4b17 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLch; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs index 7f2d18480..cef63e73d 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index dc63e7a67..04aee4897 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs index 6c4b6ca53..31e1e218e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index ca8b31d03..e6847beaf 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLab; - using ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv; - using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index 9cfa8f0c3..637c121ea 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index 9e4a8d9c3..dbc31c52b 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Hsl; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index 80b235894..640461505 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Hsv; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs index b6d9d4cb9..f5ab4d645 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 5fcc2cd5e..7b45704af 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs index 8d888182f..ac3adee63 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index 1cfd565e8..de13b97eb 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index 6660f579a..8da7dcb7e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Allows conversion to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs index a52207cb4..f86f50538 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using System.Numerics; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs index aead65e59..c5d91f9a0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Chromatic adaptation. /// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M] diff --git a/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs b/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs index 920fba18f..9ef24b38a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion +namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Converts color between two color spaces. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs index 71b1596ca..75689d997 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs index 8d877503a..33a8dc7c8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs index 5b63b167e..3884d9480 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs index b93dce75a..50332ebdc 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLch -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs index 3e399016a..ceadb0195 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs index 5339f1bcd..d30151920 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuv -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs index 36c458828..4fb8fdc80 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv -{ - using System.Runtime.CompilerServices; - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs index 3883f3a1e..82b1b1d3f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieLuv -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce +{ /// /// Converts from to . /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs index dc4a5ccf4..31868ec11 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.CieXyy -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce +{ /// /// Color converter between CIE XYZ and CIE xyY /// for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs index b50e89b18..404bc811f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Cmyk -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce +{ /// /// Color converter between CMYK and Rgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs index 6c72cf294..917cd3bf8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsl -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce +{ /// /// Color converter between HSL and Rgb /// See for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs index 54c8e405f..6219533ca 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Hsv -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce +{ /// /// Color converter between HSV and Rgb /// See for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs index 19fc78e9a..85b0efd16 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +{ /// /// The base class for converting between and color spaces. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs index 7f2df8336..8905a0a30 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +{ /// /// Color converter between CieXyz and HunterLab /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs index af7f4f370..2cd379d85 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.HunterLab -{ - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +{ /// /// Color converter between HunterLab and CieXyz /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs index c856c7ff7..780c9e5a6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Lms -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce +{ /// /// Color converter between CIE XYZ and LMS /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs index 279044baa..1bd0c4ad5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Numerics; // ReSharper disable InconsistentNaming -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Lms +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce { - using System.Numerics; - /// /// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain. /// Used in diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs index c5c612409..fd76a30fb 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ImageSharp.ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between CieXyz and LinearRgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs index 08200ce6b..21a80225e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements gamma companding /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs index bbf12ca00..c73a3486e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements L* companding /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs index 4c46e4d8c..2ec79b353 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Provides base methods for converting between Rgb and CieXyz color spaces. /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs index 4a04185e8..19d413037 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ImageSharp.ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between LinearRgb and CieXyz /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs index cf4638ae7..29ea0f314 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between LinearRgb and Rgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs index 0148b91d5..d279aba85 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System; +using System; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Represents the chromaticity coordinates of RGB primaries. /// One of the specifiers of . diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs index 630faedca..f393b133c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements Rec. 2020 companding function (for 12-bits). /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs index 24dd6ca17..ba1b1379e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements the Rec. 709 companding function /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs index 0d0d58828..e40ecc192 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs @@ -1,14 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Numerics; - - using Rgb = ColorSpaces.Rgb; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Color converter between Rgb and LinearRgb /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs index 89e403e76..8a2c66a80 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce { /// /// Trivial implementation of diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs index 18ec94c51..1e49c1159 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.Rgb -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +{ /// /// Implements sRGB companding /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs index e58da580d..f552acbb4 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion.Implementation.YCbCr -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.ColorSpaces; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce +{ /// /// Color converter between YCbCr and Rgb /// See for formulas. diff --git a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs index 55d1d65a3..6edae9301 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces.Conversion -{ - using System.Numerics; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ /// /// Basic implementation of the von Kries chromatic adaptation model /// diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs index 5bbbeec30..cf880f154 100644 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Hsl.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents a Hsl (hue, saturation, lightness) color. /// diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs index dcbb73692..9f4739379 100644 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Hsv.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index 2b49ee944..b5ba7c86c 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/HunterLab.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an Hunter LAB color. /// diff --git a/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs b/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs index a67eaeb06..08c2dafbc 100644 --- a/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs +++ b/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; +using System; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Defines a generalized method that a value type or class implements to create /// a type-specific method for determining approximate equality of instances. /// /// The type of objects to compare. /// The object specifying the type to specify precision with. - public interface IAlmostEquatable + internal interface IAlmostEquatable where TPrecision : struct, IComparable { /// diff --git a/src/ImageSharp/ColorSpaces/IColorVector.cs b/src/ImageSharp/ColorSpaces/IColorVector.cs index 08b70e482..85c040b86 100644 --- a/src/ImageSharp/ColorSpaces/IColorVector.cs +++ b/src/ImageSharp/ColorSpaces/IColorVector.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Color represented as a vector in its color space /// - public interface IColorVector + internal interface IColorVector { /// /// Gets the vector representation of the color diff --git a/src/ImageSharp/ColorSpaces/ICompanding.cs b/src/ImageSharp/ColorSpaces/ICompanding.cs index e4f0e4a4c..2dfa575ed 100644 --- a/src/ImageSharp/ColorSpaces/ICompanding.cs +++ b/src/ImageSharp/ColorSpaces/ICompanding.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces +namespace SixLabors.ImageSharp.ColorSpaces { /// /// Pair of companding functions for . diff --git a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs index 236085434..156e94ed3 100644 --- a/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs +++ b/src/ImageSharp/ColorSpaces/IRgbWorkingSpace.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; +using System; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Encasulates the RGB working color space /// diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs index 224cf9939..85c4063bf 100644 --- a/src/ImageSharp/ColorSpaces/Illuminants.cs +++ b/src/ImageSharp/ColorSpaces/Illuminants.cs @@ -1,4 +1,7 @@ -namespace ImageSharp.ColorSpaces +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.ColorSpaces { /// /// The well known standard illuminants. diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index fdfc0d266..07889c352 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an linear Rgb color with specified working space /// diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs index c76d87253..82c291de3 100644 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ b/src/ImageSharp/ColorSpaces/Lms.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// LMS is a color space represented by the response of the three types of cones of the human eye, /// named after their responsivity (sensitivity) at long, medium and short wavelengths. diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index 898c81730..8ac8411b2 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an RGB color with specified working space /// diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 20b937394..098ca9a4a 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; // ReSharper disable InconsistentNaming -namespace ImageSharp.ColorSpaces +namespace SixLabors.ImageSharp.ColorSpaces { - using ImageSharp.ColorSpaces.Conversion.Implementation.Rgb; - /// /// Chromaticity coordinates taken from: /// diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs index cbba02305..708a74308 100644 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/YCbCr.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.ColorSpaces -{ - using System; - using System.ComponentModel; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.ColorSpaces +{ /// /// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg. /// diff --git a/src/ImageSharp/Common/Constants.cs b/src/ImageSharp/Common/Constants.cs index cf43951bc..41f2bce24 100644 --- a/src/ImageSharp/Common/Constants.cs +++ b/src/ImageSharp/Common/Constants.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Common constants used throughout the project diff --git a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs index 32a085435..7a91756b6 100644 --- a/src/ImageSharp/Common/Exceptions/ImageFormatException.cs +++ b/src/ImageSharp/Common/Exceptions/ImageFormatException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp +{ /// /// The exception that is thrown when the library tries to load /// an image, which has an invalid format. diff --git a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs index ef84a1e39..eb50d0b65 100644 --- a/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs +++ b/src/ImageSharp/Common/Exceptions/ImageProcessingException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp +{ /// /// The exception that is thrown when an error occurs when applying a process to an image. /// diff --git a/src/ImageSharp/Common/Extensions/ByteExtensions.cs b/src/ImageSharp/Common/Extensions/ByteExtensions.cs index f1161eb6f..f6c720795 100644 --- a/src/ImageSharp/Common/Extensions/ByteExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ByteExtensions.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the struct buffers. /// diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs index d46330102..8bebb3de7 100644 --- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for classes that implement . /// diff --git a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs index af8e50c7b..e97a2eaed 100644 --- a/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/EnumerableExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Common +{ /// /// Encapsulates a series of time saving extension methods to the interface. /// - public static class EnumerableExtensions + internal static class EnumerableExtensions { /// /// Generates a sequence of integral numbers within a specified range. diff --git a/src/ImageSharp/Common/Extensions/ListExtensions.cs b/src/ImageSharp/Common/Extensions/ListExtensions.cs new file mode 100644 index 000000000..2713896c0 --- /dev/null +++ b/src/ImageSharp/Common/Extensions/ListExtensions.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Common.Extensions +{ + /// + /// Encapsulates a series of time saving extension methods to the class. + /// + internal static class ListExtensions + { + /// + /// Inserts an item at the given index automatically expanding the capacity if required. + /// + /// The type of object within the list + /// The list + /// The index + /// The item to insert + public static void SafeInsert(this List list, int index, T item) + { + if (index >= list.Count) + { + list.Add(item); + } + else + { + list[index] = item; + } + } + + /// + /// Removes the last element from a list and returns that element. This method changes the length of the list. + /// + /// The type of object within the list + /// The list + /// The last element in the specified sequence. + public static T Pop(this List list) + { + int last = list.Count - 1; + T item = list[last]; + list.RemoveAt(last); + return item; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs new file mode 100644 index 000000000..cb80a672a --- /dev/null +++ b/src/ImageSharp/Common/Extensions/SimdUtils.cs @@ -0,0 +1,232 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Various extension and utility methods for and utilizing SIMD capabilities + /// + internal static class SimdUtils + { + /// + /// Indicates AVX2 architecture where both float and integer registers are of size 256 byte. + /// + public static readonly bool IsAvx2 = Vector.Count == 8 && Vector.Count == 8; + + internal static void GuardAvx2(string operation) + { + if (!IsAvx2) + { + throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!"); + } + } + + /// + /// Transform all scalars in 'v' in a way that converting them to would have rounding semantics. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 PseudoRound(this Vector4 v) + { + var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1)); + + return v + (sign * 0.5f); + } + + /// + /// Rounds all values in 'v' to the nearest integer following semantics. + /// Source: + /// + /// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110 + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector FastRound(this Vector x) + { + Vector magic0 = new Vector(int.MinValue); // 0x80000000 + Vector sgn0 = Vector.AsVectorSingle(magic0); + Vector and0 = Vector.BitwiseAnd(sgn0, x); + Vector or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); + Vector add0 = Vector.Add(x, or0); + Vector sub0 = Vector.Subtract(add0, or0); + return sub0; + } + + /// + /// Convert 'source.Length' values normalized into [0..1] from 'source' into 'dest' buffer of values. + /// The values gonna be scaled up into [0-255] and rounded. + /// Based on: + /// + /// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions + /// + /// + internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan source, Span dest) + { + GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); + + DebugGuard.IsTrue((source.Length % Vector.Count) == 0, nameof(source), "source.Length should be divisable by Vector.Count!"); + + if (source.Length == 0) + { + return; + } + + ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); + ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + int n = source.Length / 8; + + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + // need to copy to a temporal struct, because + // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) + // does not work. TODO: This might be a CoreClr bug, need to ask/report + var temp = default(Octet.OfUInt32); + ref Vector tempRef = ref Unsafe.As>(ref temp); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + Vector x = Unsafe.Add(ref srcBase, i); + x = (x * scale) + magick; + tempRef = x; + + ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); + d.LoadFrom(ref temp); + } + } + + /// + /// Same as but clamps overflown values before conversion. + /// + internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest) + { + GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); + + DebugGuard.IsTrue((source.Length % Vector.Count) == 0, nameof(source), "source.Length should be divisable by Vector.Count!"); + + if (source.Length == 0) + { + return; + } + + ref Vector srcBase = ref Unsafe.As>(ref source.DangerousGetPinnableReference()); + ref Octet.OfByte destBase = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + int n = source.Length / 8; + + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + // need to copy to a temporal struct, because + // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) + // does not work. TODO: This might be a CoreClr bug, need to ask/report + var temp = default(Octet.OfUInt32); + ref Vector tempRef = ref Unsafe.As>(ref temp); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + Vector x = Unsafe.Add(ref srcBase, i); + x = Vector.Max(x, Vector.Zero); + x = Vector.Min(x, Vector.One); + + x = (x * scale) + magick; + tempRef = x; + + ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); + d.LoadFrom(ref temp); + } + } + + // TODO: Replace these with T4-d library level tuples! + internal static class Octet + { + [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] + public struct OfUInt32 + { + [FieldOffset(0 * sizeof(uint))] + public uint V0; + + [FieldOffset(1 * sizeof(uint))] + public uint V1; + + [FieldOffset(2 * sizeof(uint))] + public uint V2; + + [FieldOffset(3 * sizeof(uint))] + public uint V3; + + [FieldOffset(4 * sizeof(uint))] + public uint V4; + + [FieldOffset(5 * sizeof(uint))] + public uint V5; + + [FieldOffset(6 * sizeof(uint))] + public uint V6; + + [FieldOffset(7 * sizeof(uint))] + public uint V7; + + public override string ToString() + { + return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; + } + } + + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct OfByte + { + [FieldOffset(0)] + public byte V0; + + [FieldOffset(1)] + public byte V1; + + [FieldOffset(2)] + public byte V2; + + [FieldOffset(3)] + public byte V3; + + [FieldOffset(4)] + public byte V4; + + [FieldOffset(5)] + public byte V5; + + [FieldOffset(6)] + public byte V6; + + [FieldOffset(7)] + public byte V7; + + public override string ToString() + { + return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; + } + + public void LoadFrom(ref OfUInt32 i) + { + this.V0 = (byte)i.V0; + this.V1 = (byte)i.V1; + this.V2 = (byte)i.V2; + this.V3 = (byte)i.V3; + this.V4 = (byte)i.V4; + this.V5 = (byte)i.V5; + this.V6 = (byte)i.V6; + this.V7 = (byte)i.V7; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs index 6de94dd22..d8fd45440 100644 --- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs +++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Buffers; - using System.IO; +using System.Buffers; +using System.IO; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -27,7 +25,7 @@ namespace ImageSharp if (stream.CanSeek) { - stream.Position += count; + stream.Seek(count, SeekOrigin.Current); // Position += count; } else { diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs index fac33da14..5fbc3960a 100644 --- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs +++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the struct. /// diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index f6941fc6f..dc3cff7a2 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Diagnostics; +using System; +using System.Diagnostics; +namespace SixLabors.ImageSharp +{ /// /// Provides methods to protect against invalid parameters for a DEBUG build. /// diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 8e55c18af..b0546bf9a 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +namespace SixLabors.ImageSharp +{ /// /// Provides methods to protect against invalid parameters. /// diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 367aa12d0..8717fa987 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Provides common mathematical methods. /// @@ -131,10 +128,10 @@ namespace ImageSharp /// public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) { - Vector2 leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); - Vector2 rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); - Vector2 leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); - Vector2 rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); + var leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); + var rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); + var leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); + var rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom }; float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min(); @@ -153,7 +150,7 @@ namespace ImageSharp /// /// The . /// - public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) + public static Rectangle GetFilteredBoundingRectangle(ImageFrame bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) where TPixel : struct, IPixel { int width = bitmap.Width; @@ -161,7 +158,7 @@ namespace ImageSharp var topLeft = default(Point); var bottomRight = default(Point); - Func, int, int, float, bool> delegateFunc; + Func, int, int, float, bool> delegateFunc; // Determine which channel to check against switch (channel) @@ -183,7 +180,7 @@ namespace ImageSharp break; } - int GetMinY(ImageBase pixels) + int GetMinY(ImageFrame pixels) { for (int y = 0; y < height; y++) { @@ -199,7 +196,7 @@ namespace ImageSharp return 0; } - int GetMaxY(ImageBase pixels) + int GetMaxY(ImageFrame pixels) { for (int y = height - 1; y > -1; y--) { @@ -215,7 +212,7 @@ namespace ImageSharp return height; } - int GetMinX(ImageBase pixels) + int GetMinX(ImageFrame pixels) { for (int x = 0; x < width; x++) { @@ -231,7 +228,7 @@ namespace ImageSharp return 0; } - int GetMaxX(ImageBase pixels) + int GetMaxX(ImageFrame pixels) { for (int x = width - 1; x > -1; x--) { diff --git a/src/ImageSharp/Common/Helpers/MathF.cs b/src/ImageSharp/Common/Helpers/MathF.cs index 5ce5b5d5d..7d2f8063f 100644 --- a/src/ImageSharp/Common/Helpers/MathF.cs +++ b/src/ImageSharp/Common/Helpers/MathF.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp +{ /// /// Provides single-precision floating point constants and static methods for trigonometric, logarithmic, and other common mathematical functions. /// diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index b0291dec5..740103533 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -1,19 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - - using Formats; - using ImageSharp.IO; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.IO; +namespace SixLabors.ImageSharp +{ /// /// Provides initialization code which allows extending the library. /// @@ -103,11 +104,16 @@ namespace ImageSharp #if !NETSTANDARD1_1 /// - /// Gets or sets the fielsystem helper for accessing the local file system. + /// Gets or sets the filesystem helper for accessing the local file system. /// internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); #endif + /// + /// Gets or sets the image operations provider factory. + /// + internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); + /// /// Registers a new format provider. /// @@ -121,7 +127,7 @@ namespace ImageSharp /// /// Registers a new format provider. /// - /// The format to register as a well know format. + /// The format to register as a known format. public void AddImageFormat(IImageFormat format) { Guard.NotNull(format, nameof(format)); @@ -135,7 +141,7 @@ namespace ImageSharp /// /// The extension to discover /// The if found otherwise null - public IImageFormat FindFormatByFileExtensions(string extension) + public IImageFormat FindFormatByFileExtension(string extension) { return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); } @@ -144,7 +150,7 @@ namespace ImageSharp /// For the specified mime type find the . /// /// The mime-type to discover - /// The if found otherwise null + /// The if found; otherwise null public IImageFormat FindFormatByMimeType(string mimeType) { return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); diff --git a/src/ImageSharp/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/DefaultInternalImageProcessorContext.cs new file mode 100644 index 000000000..575525a77 --- /dev/null +++ b/src/ImageSharp/DefaultInternalImageProcessorContext.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Performs processor application operations on the source image + /// + /// The pixel format + internal class DefaultInternalImageProcessorContext : IInternalImageProcessingContext + where TPixel : struct, IPixel + { + private readonly bool mutate; + private readonly Image source; + private Image destination; + + /// + /// Initializes a new instance of the class. + /// + /// The image. + /// The mutate. + public DefaultInternalImageProcessorContext(Image source, bool mutate) + { + this.mutate = mutate; + this.source = source; + if (this.mutate) + { + this.destination = source; + } + } + + /// + public Image Apply() + { + if (!this.mutate && this.destination == null) + { + // Ensure we have cloned it if we are not mutating as we might have failed to register any processors + this.destination = this.source.Clone(); + } + + return this.destination; + } + + /// + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + if (!this.mutate && this.destination == null) + { + // This will only work if the first processor applied is the cloning one thus + // realistically for this optermissation to work the resize must the first processor + // applied any only up processors will take the douple data path. + if (processor is ICloningImageProcessor cloningImageProcessor) + { + this.destination = cloningImageProcessor.CloneAndApply(this.source, rectangle); + return this; + } + + this.destination = this.source.Clone(); + } + + processor.Apply(this.destination, rectangle); + return this; + } + + /// + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + return this.ApplyProcessor(processor, this.source.Bounds()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs similarity index 72% rename from src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs index 629944ea1..3899b14cc 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/AtkinsonDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Atkinson image dithering algorithm. /// /// - public sealed class Atkinson : ErrorDiffuser + public sealed class AtkinsonDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Atkinson() + public AtkinsonDiffuser() : base(AtkinsonMatrix, 8) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs similarity index 65% rename from src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs index 02d41c369..4d9f4d3c4 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/BurksDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Burks image dithering algorithm. /// /// - public sealed class Burks : ErrorDiffuser + public sealed class BurksDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Burks() + public BurksDiffuser() : base(BurksMatrix, 32) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs similarity index 78% rename from src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs index 408e6c383..510a097ea 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs @@ -1,21 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering.Base +{ /// /// The base class for performing error diffusion based dithering. /// - public abstract class ErrorDiffuser : IErrorDiffuser + public abstract class ErrorDiffuserBase : IErrorDiffuser { /// /// The vector to perform division. @@ -43,11 +41,11 @@ namespace ImageSharp.Dithering private readonly Fast2DArray matrix; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The dithering matrix. /// The divisor. - internal ErrorDiffuser(Fast2DArray matrix, byte divisor) + internal ErrorDiffuserBase(Fast2DArray matrix, byte divisor) { Guard.NotNull(matrix, nameof(matrix)); Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); @@ -72,15 +70,15 @@ namespace ImageSharp.Dithering /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageBase pixels, TPixel source, TPixel transformed, int x, int y, int width, int height) + public void Dither(ImageFrame pixels, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel { - this.Dither(pixels, source, transformed, x, y, width, height, true); + this.Dither(pixels, source, transformed, x, y, minX, minY, maxX, maxY, true); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel) + public void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) where TPixel : struct, IPixel { if (replacePixel) @@ -96,15 +94,15 @@ namespace ImageSharp.Dithering for (int row = 0; row < this.matrixHeight; row++) { int matrixY = y + row; - if (matrixY > 0 && matrixY < height) + if (matrixY > minY && matrixY < maxY) { - Span rowSpan = image.GetRowSpan(matrixY); + Span rowSpan = image.GetPixelRowSpan(matrixY); for (int col = 0; col < this.matrixWidth; col++) { int matrixX = x + (col - this.startingOffset); - if (matrixX > 0 && matrixX < width) + if (matrixX > minX && matrixX < maxX) { float coefficient = this.matrix[row, col]; diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs similarity index 70% rename from src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs index 6165da634..6457fbe01 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinbergDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm. /// /// - public sealed class FloydSteinberg : ErrorDiffuser + public sealed class FloydSteinbergDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public FloydSteinberg() + public FloydSteinbergDiffuser() : base(FloydSteinbergMatrix, 16) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index bc785e897..c538d643c 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Encapsulates properties and methods required to perfom diffused error dithering on an image. /// @@ -20,10 +18,12 @@ namespace ImageSharp.Dithering /// The transformed pixel /// The column index. /// The row index. - /// The image width. - /// The image height. + /// The minimum column value. + /// The minimum row value. + /// The maximum column value. + /// The maximum row value. /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int width, int height) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY) where TPixel : struct, IPixel; /// @@ -34,14 +34,16 @@ namespace ImageSharp.Dithering /// The transformed pixel /// The column index. /// The row index. - /// The image width. - /// The image height. + /// The minimum column value. + /// The minimum row value. + /// The maximum column value. + /// The maximum row value. /// /// Whether to replace the pixel at the given coordinates with the transformed value. /// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false. /// /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel) + void Dither(ImageFrame image, TPixel source, TPixel transformed, int x, int y, int minX, int minY, int maxX, int maxY, bool replacePixel) where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs similarity index 70% rename from src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs index 6daeab32f..30e09b47a 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinkeDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm. /// /// - public sealed class JarvisJudiceNinke : ErrorDiffuser + public sealed class JarvisJudiceNinkeDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public JarvisJudiceNinke() + public JarvisJudiceNinkeDiffuser() : base(JarvisJudiceNinkeMatrix, 48) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs similarity index 71% rename from src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs index 0c0944d0e..c472d25b0 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2Diffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Sierra2 image dithering algorithm. /// /// - public sealed class Sierra2 : ErrorDiffuser + public sealed class Sierra2Diffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Sierra2() + public Sierra2Diffuser() : base(Sierra2Matrix, 16) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs similarity index 72% rename from src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs index 2e2220846..c19ab2aaa 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3Diffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Sierra3 image dithering algorithm. /// /// - public sealed class Sierra3 : ErrorDiffuser + public sealed class Sierra3Diffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Sierra3() + public Sierra3Diffuser() : base(Sierra3Matrix, 32) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs similarity index 70% rename from src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs index fe4c933a9..263bae568 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLiteDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the SierraLite image dithering algorithm. /// /// - public sealed class SierraLite : ErrorDiffuser + public sealed class SierraLiteDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -24,9 +23,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public SierraLite() + public SierraLiteDiffuser() : base(SierraLiteMatrix, 4) { } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs similarity index 66% rename from src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs index b04c16481..071769506 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/StuckiDiffuser.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the Stucki image dithering algorithm. /// /// - public sealed class Stucki : ErrorDiffuser + public sealed class StuckiDiffuser : ErrorDiffuserBase { /// /// The diffusion matrix @@ -25,9 +24,9 @@ namespace ImageSharp.Dithering }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Stucki() + public StuckiDiffuser() : base(StuckiMatrix, 42) { } diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/BayerDither.cs similarity index 70% rename from src/ImageSharp/Dithering/Ordered/Bayer.cs rename to src/ImageSharp/Dithering/Ordered/BayerDither.cs index ded17d1e1..685dca5fe 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/BayerDither.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering.Ordered -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix. /// /// - public sealed class Bayer : OrderedDither4x4 + public sealed class BayerDither : OrderedDitherBase { /// /// The threshold matrix. @@ -27,9 +26,9 @@ namespace ImageSharp.Dithering.Ordered }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Bayer() + public BayerDither() : base(ThresholdMatrix) { } diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index c69cddefe..e0e11ad9e 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Encapsulates properties and methods required to perfom ordered dithering on an image. /// @@ -23,10 +21,8 @@ namespace ImageSharp.Dithering /// The component index to test the threshold against. Must range from 0 to 3. /// The column index. /// The row index. - /// The image width. - /// The image height. /// The pixel format. - void Dither(ImageBase image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height) + void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs similarity index 74% rename from src/ImageSharp/Dithering/Ordered/Ordered.cs rename to src/ImageSharp/Dithering/Ordered/OrderedDither.cs index 1fd39eb8b..12968914d 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering.Ordered -{ - using ImageSharp.Memory; +using SixLabors.ImageSharp.Dithering.Base; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Dithering +{ /// /// Applies error diffusion based dithering using the 4x4 ordered dithering matrix. /// /// - public sealed class Ordered : OrderedDither4x4 + public sealed class OrderedDither : OrderedDitherBase { /// /// The threshold matrix. @@ -27,9 +26,9 @@ namespace ImageSharp.Dithering.Ordered }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public Ordered() + public OrderedDither() : base(ThresholdMatrix) { } diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs similarity index 64% rename from src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs rename to src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs index a180888f7..09c30eb27 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Dithering.Ordered -{ - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Dithering.Base +{ /// /// The base class for performing ordered ditheroing using a 4x4 matrix. /// - public abstract class OrderedDither4x4 : IOrderedDither + public abstract class OrderedDitherBase : IOrderedDither { /// /// The dithering matrix @@ -19,16 +17,16 @@ namespace ImageSharp.Dithering.Ordered private Fast2DArray matrix; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The thresholding matrix. - internal OrderedDither4x4(Fast2DArray matrix) + internal OrderedDitherBase(Fast2DArray matrix) { this.matrix = matrix; } /// - public void Dither(ImageBase image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height) + public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y) where TPixel : struct, IPixel { // TODO: This doesn't really cut it for me. diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 330326acf..d08487cf2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Enumerates the available bits per pixel for bitmap. diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index a9246d449..1280498ac 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Defines how the compression type of the image data diff --git a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs index f70ff1a56..b091467bf 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Registers the image encoders, decoders and mime type detectors for the bmp format. @@ -13,8 +11,8 @@ namespace ImageSharp.Formats /// public void Configure(Configuration config) { - config.SetEncoder(ImageFormats.Bitmap, new BmpEncoder()); - config.SetDecoder(ImageFormats.Bitmap, new BmpDecoder()); + config.SetEncoder(ImageFormats.Bmp, new BmpEncoder()); + config.SetDecoder(ImageFormats.Bmp, new BmpDecoder()); config.AddImageFormatDetector(new BmpImageFormatDetector()); } } diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs index d394b61f6..b7291bb99 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Defines constants relating to BMPs /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 5baf1b1a5..8b53194fd 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image decoder for generating an image out of a Windows bitmap stream. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 3dcbf4075..625b2a3a8 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.IO; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Performs the bmp decoding operation. /// @@ -123,11 +121,11 @@ namespace ImageSharp.Formats this.currentStream.Read(palette, 0, colorMapSize); } - if (this.infoHeader.Width > Image.MaxWidth || this.infoHeader.Height > Image.MaxHeight) + if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue) { throw new ArgumentOutOfRangeException( $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " - + $"bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); + + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'"); } Image image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index dfba0b41c..366afceb5 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 30961646f..d34d17084 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - - using ImageSharp.PixelFormats; - - using IO; +using System; +using System.IO; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// @@ -37,12 +33,12 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. - public void Encode(ImageBase image, Stream stream) + public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); @@ -77,7 +73,7 @@ namespace ImageSharp.Formats WriteHeader(writer, fileHeader); this.WriteInfo(writer, infoHeader); - this.WriteImage(writer, image); + this.WriteImage(writer, image.Frames.RootFrame); writer.Flush(); } @@ -129,9 +125,9 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing the stream to write to. /// - /// The containing pixel data. + /// The containing pixel data. /// - private void WriteImage(EndianBinaryWriter writer, ImageBase image) + private void WriteImage(EndianBinaryWriter writer, ImageFrame image) where TPixel : struct, IPixel { using (PixelAccessor pixels = image.Lock()) diff --git a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs index f9b20a48f..4255ecae4 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// Stores general information about the Bitmap file. diff --git a/src/ImageSharp/Formats/Bmp/BmpFormat.cs b/src/ImageSharp/Formats/Bmp/BmpFormat.cs index bd25eb9b7..64c6574c1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpFormat.cs +++ b/src/ImageSharp/Formats/Bmp/BmpFormat.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Registers the image encoders, decoders and mime type detectors for the bmp format. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index 697ee0f98..9c9786e0a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Detects bmp file headers /// @@ -20,7 +18,7 @@ namespace ImageSharp.Formats { if (this.IsSupportedFileFormat(header)) { - return ImageFormats.Bitmap; + return ImageFormats.Bmp; } return null; diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index c920bfbbe..b24404cac 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -1,8 +1,6 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Bmp { /// /// This block of bytes tells the application detailed information diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs index 9285b9cf7..920c9ce02 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Image decoder options for decoding Windows bitmap streams. /// diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index dd17043fa..c4e219889 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Bmp +{ /// /// Configuration options for use during bmp encoding /// diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index aba24f999..935ce8f4a 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,11 +22,20 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsBmp(this Image source, Stream stream) + public static void SaveAsBmp(this Image source, Stream stream) + where TPixel : struct, IPixel + => source.SaveAsBmp(stream, null); + + /// + /// Saves the image to the given stream with the bmp format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream is null. + public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) where TPixel : struct, IPixel - => source.Save(stream, new BmpEncoder()); + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Bmp)); } } diff --git a/src/ImageSharp/Formats/Gif/DisposalMethod.cs b/src/ImageSharp/Formats/Gif/DisposalMethod.cs index 80b5f3c6e..f553c204b 100644 --- a/src/ImageSharp/Formats/Gif/DisposalMethod.cs +++ b/src/ImageSharp/Formats/Gif/DisposalMethod.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// Provides enumeration for instructing the decoder what to do with the last image diff --git a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs index ee134d66c..4c42a833c 100644 --- a/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs +++ b/src/ImageSharp/Formats/Gif/GifConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// Registers the image encoders, decoders and mime type detectors for the gif format. diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs index 9bec6c48f..d448cf783 100644 --- a/src/ImageSharp/Formats/Gif/GifConstants.cs +++ b/src/ImageSharp/Formats/Gif/GifConstants.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.Text; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Constants that define specific points within a gif. /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 927289094..5ded251e2 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Decoder for generating an image out of a gif encoded stream. /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 948103fed..c3c395e85 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -1,19 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Gif { - using System; - using System.Buffers; - using System.IO; - using System.Runtime.CompilerServices; - using System.Text; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - /// /// Performs the gif decoding operation. /// @@ -370,7 +369,7 @@ namespace ImageSharp.Formats ImageFrame currentFrame = null; - ImageBase image; + ImageFrame image; if (this.previousFrame == null) { @@ -379,7 +378,7 @@ namespace ImageSharp.Formats this.SetFrameMetaData(this.metaData); - image = this.image; + image = this.image.Frames.RootFrame; } else { @@ -442,7 +441,7 @@ namespace ImageSharp.Formats writeY = y; } - Span rowSpan = image.GetRowSpan(writeY); + Span rowSpan = image.GetPixelRowSpan(writeY); Rgba32 rgba = new Rgba32(0, 0, 0, 255); @@ -472,7 +471,7 @@ namespace ImageSharp.Formats return; } - this.previousFrame = currentFrame == null ? this.image.ToFrame() : currentFrame; + this.previousFrame = currentFrame == null ? this.image.Frames.RootFrame : currentFrame; if (this.graphicsControlExtension != null && this.graphicsControlExtension.DisposalMethod == DisposalMethod.RestoreToBackground) @@ -485,7 +484,7 @@ namespace ImageSharp.Formats /// Restores the current frame area to the background. /// /// The frame. - private void RestoreToBackground(ImageBase frame) + private void RestoreToBackground(ImageFrame frame) { if (this.restoreArea == null) { @@ -523,7 +522,7 @@ namespace ImageSharp.Formats /// /// The meta data. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SetFrameMetaData(IMetaData metaData) + private void SetFrameMetaData(IFrameMetaData metaData) { if (this.graphicsControlExtension != null) { diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index b48db5635..ccf46a17d 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Image encoder for writing image data to a stream in gif format. /// @@ -46,7 +44,7 @@ namespace ImageSharp.Formats public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { - GifEncoderCore encoder = new GifEncoderCore(this); + var encoder = new GifEncoderCore(this); encoder.Encode(image, stream); } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 81b3cfba4..d143cd531 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.IO; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; + +namespace SixLabors.ImageSharp.Formats.Gif { - using System; - using System.Buffers; - using System.IO; - using System.Linq; - using System.Text; - using ImageSharp.PixelFormats; - - using IO; - using Quantizers; - /// /// Performs the gif encoding operation. /// @@ -92,20 +90,20 @@ namespace ImageSharp.Formats var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); // Ensure that pallete size can be set but has a fallback. - int paletteSize = this.paletteSize; - paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256; + int size = this.paletteSize; + size = size > 0 ? size.Clamp(1, 256) : 256; // Get the number of bits. - this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize); + this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(size); - this.hasFrames = image.Frames.Any(); + this.hasFrames = image.Frames.Count > 1; // Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames. var ditheredQuantizer = (IQuantizer)this.quantizer; ditheredQuantizer.Dither = !this.hasFrames; // Quantize the image returning a palette. - QuantizedImage quantized = ditheredQuantizer.Quantize(image, paletteSize); + QuantizedImage quantized = ditheredQuantizer.Quantize(image.Frames.RootFrame, size); int index = this.GetTransparentIndex(quantized); @@ -116,28 +114,27 @@ namespace ImageSharp.Formats this.WriteLogicalScreenDescriptor(image, writer, index); // Write the first frame. - this.WriteGraphicalControlExtension(image.MetaData, writer, index); this.WriteComments(image, writer); - this.WriteImageDescriptor(image, writer); - this.WriteColorTable(quantized, writer); - this.WriteImageData(quantized, writer); // Write additional frames. if (this.hasFrames) { this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count); + } - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < image.Frames.Count; i++) + foreach (ImageFrame frame in image.Frames) + { + if (quantized == null) { - ImageFrame frame = image.Frames[i]; - QuantizedImage quantizedFrame = ditheredQuantizer.Quantize(frame, paletteSize); - - this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame)); - this.WriteImageDescriptor(frame, writer); - this.WriteColorTable(quantizedFrame, writer); - this.WriteImageData(quantizedFrame, writer); + quantized = ditheredQuantizer.Quantize(frame, size); } + + this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantized)); + this.WriteImageDescriptor(frame, writer); + this.WriteColorTable(quantized, writer); + this.WriteImageData(quantized, writer); + + quantized = null; // so next frame can regenerate it } // TODO: Write extension etc @@ -255,7 +252,7 @@ namespace ImageSharp.Formats /// Writes the image comments to the stream. /// /// The pixel format. - /// The to be encoded. + /// The to be encoded. /// The stream to write to. private void WriteComments(Image image, EndianBinaryWriter writer) where TPixel : struct, IPixel @@ -290,7 +287,7 @@ namespace ImageSharp.Formats /// The metadata of the image or frame. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) + private void WriteGraphicalControlExtension(IFrameMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) { var extension = new GifGraphicsControlExtension { @@ -323,9 +320,9 @@ namespace ImageSharp.Formats /// Writes the image descriptor to the stream. /// /// The pixel format. - /// The to be encoded. + /// The to be encoded. /// The stream to write to. - private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) + private void WriteImageDescriptor(ImageFrame image, EndianBinaryWriter writer) where TPixel : struct, IPixel { writer.Write(GifConstants.ImageDescriptorLabel); // 2c @@ -349,7 +346,7 @@ namespace ImageSharp.Formats /// Writes the color table to the stream. /// /// The pixel format. - /// The to encode. + /// The to encode. /// The writer to write to the stream with. private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs index 744aadff9..6353690f4 100644 --- a/src/ImageSharp/Formats/Gif/GifFormat.cs +++ b/src/ImageSharp/Formats/Gif/GifFormat.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Registers the image encoders, decoders and mime type detectors for the gif format. /// diff --git a/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs b/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs index 04fcfc516..36346f606 100644 --- a/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Gif/GifImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Detects gif file headers /// diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs index caaa8932b..60c39f936 100644 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Decoder for generating an image out of a gif encoded stream. /// diff --git a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs index c38ec7e45..374dea659 100644 --- a/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// The configuration options used for encoding gifs /// diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index ea9c9b504..939eb456e 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,14 +22,9 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsGif(this Image source, Stream stream) + public static void SaveAsGif(this Image source, Stream stream) where TPixel : struct, IPixel - { - return SaveAsGif(source, stream, null); - } + => source.SaveAsGif(stream, null); /// /// Saves the image to the given stream with the gif format. @@ -41,16 +34,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsGif(this Image source, Stream stream, GifEncoder encoder) + public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) where TPixel : struct, IPixel - { - encoder = encoder ?? new GifEncoder(); - encoder.Encode(source, stream); - - return source; - } + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Gif)); } } diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index bc0e9717c..b8f12f930 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Buffers; - using System.IO; +using System; +using System.Buffers; +using System.IO; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Decompresses and decodes data using the dynamic LZW algorithms. /// diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index 69419444f..b7bfd0fd2 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Buffers; - using System.IO; +using System; +using System.Buffers; +using System.IO; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Encodes and compresses the image data using dynamic Lempel-Ziv compression. /// diff --git a/src/ImageSharp/Formats/Gif/PackedField.cs b/src/ImageSharp/Formats/Gif/PackedField.cs index 21d8f91f2..962e2082b 100644 --- a/src/ImageSharp/Formats/Gif/PackedField.cs +++ b/src/ImageSharp/Formats/Gif/PackedField.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Gif +{ /// /// Represents a byte of data in a GIF data stream which contains a number /// of data items. diff --git a/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs index 503bd4fdf..8cdd309d3 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// The Graphic Control Extension contains parameters used when diff --git a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs index 12c13beaf..2ed9e4747 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// Each image in the Data Stream is composed of an Image Descriptor, diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs index fc2cc974a..b1109c3e2 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Gif { /// /// The Logical Screen Descriptor contains the parameters diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 66eabb1b8..e392cf7c6 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -1,23 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Encapsulates properties and methods required for decoding an image from a stream. /// public interface IImageDecoder { /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// /// The pixel format. /// The configuration for the image. diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index 4ad41ebc2..ac0b6e311 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats +{ /// /// Encapsulates properties and methods required for encoding an image to a stream. /// diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs index d6ddc0b0b..15bdc73a8 100644 --- a/src/ImageSharp/Formats/IImageFormat.cs +++ b/src/ImageSharp/Formats/IImageFormat.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats +{ /// /// Describes an image format. /// diff --git a/src/ImageSharp/Formats/IImageFormatDetector.cs b/src/ImageSharp/Formats/IImageFormatDetector.cs index a53da07e8..8266439bd 100644 --- a/src/ImageSharp/Formats/IImageFormatDetector.cs +++ b/src/ImageSharp/Formats/IImageFormatDetector.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.Text; +using System; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.Formats +{ /// /// Used for detecting mime types from a file header /// diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs new file mode 100644 index 000000000..3f4c69c3e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -0,0 +1,306 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + /// + /// Represents a Jpeg block with coefficiens. + /// + // ReSharper disable once InconsistentNaming + internal unsafe struct Block8x8 : IEquatable + { + /// + /// A number of scalar coefficients in a + /// + public const int Size = 64; + + /// + /// A fixed size buffer holding the values. + /// See: + /// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers + /// + /// + private fixed short data[Size]; + + /// + /// Initializes a new instance of the struct. + /// + /// A of coefficients + public Block8x8(Span coefficients) + { + ref byte selfRef = ref Unsafe.As(ref this); + ref byte sourceRef = ref coefficients.NonPortableCast().DangerousGetPinnableReference(); + Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short)); + } + + /// + /// Gets or sets a value at the given index + /// + /// The index + /// The value + public short this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + GuardBlockIndex(idx); + ref short selfRef = ref Unsafe.As(ref this); + return Unsafe.Add(ref selfRef, idx); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + GuardBlockIndex(idx); + ref short selfRef = ref Unsafe.As(ref this); + Unsafe.Add(ref selfRef, idx) = value; + } + } + + /// + /// Gets or sets a value in a row+coulumn of the 8x8 block + /// + /// The x position index in the row + /// The column index + /// The value + public short this[int x, int y] + { + get => this[(y * 8) + x]; + set => this[(y * 8) + x] = value; + } + + public static bool operator ==(Block8x8 left, Block8x8 right) + { + return left.Equals(right); + } + + public static bool operator !=(Block8x8 left, Block8x8 right) + { + return !left.Equals(right); + } + + /// + /// Multiply all elements by a given + /// + public static Block8x8 operator *(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val *= value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Divide all elements by a given + /// + public static Block8x8 operator /(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val /= value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Add an to all elements + /// + public static Block8x8 operator +(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val += value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Subtract an from all elements + /// + public static Block8x8 operator -(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val -= value; + result[i] = (short)val; + } + + return result; + } + + /// + /// Pointer-based "Indexer" (getter part) + /// + /// Block pointer + /// Index + /// The scaleVec value at the specified index + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short GetScalarAt(Block8x8* blockPtr, int idx) + { + GuardBlockIndex(idx); + + short* fp = blockPtr->data; + return fp[idx]; + } + + /// + /// Pointer-based "Indexer" (setter part) + /// + /// Block pointer + /// Index + /// Value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetScalarAt(Block8x8* blockPtr, int idx, short value) + { + GuardBlockIndex(idx); + + short* fp = blockPtr->data; + fp[idx] = value; + } + + /// + /// Convert into + /// + public Block8x8F AsFloatBlock() + { + // TODO: Optimize this + var result = default(Block8x8F); + for (int i = 0; i < Size; i++) + { + result[i] = this[i]; + } + + return result; + } + + /// + /// Copy all elements to an array of . + /// + public short[] ToArray() + { + short[] result = new short[Size]; + this.CopyTo(result); + return result; + } + + /// + /// Copy elements into 'destination' Span of values + /// + public void CopyTo(Span destination) + { + ref byte selfRef = ref Unsafe.As(ref this); + ref byte destRef = ref destination.NonPortableCast().DangerousGetPinnableReference(); + Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short)); + } + + /// + /// Copy elements into 'destination' Span of values + /// + public void CopyTo(Span destination) + { + for (int i = 0; i < Size; i++) + { + destination[i] = this[i]; + } + } + + /// + /// Cast and copy -s from the beginning of 'source' span. + /// + public void LoadFrom(Span source) + { + for (int i = 0; i < Size; i++) + { + this[i] = (short)source[i]; + } + } + + [Conditional("DEBUG")] + private static void GuardBlockIndex(int idx) + { + DebugGuard.MustBeLessThan(idx, Size, nameof(idx)); + DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx)); + } + + /// + public override string ToString() + { + var bld = new StringBuilder(); + bld.Append('['); + for (int i = 0; i < Size; i++) + { + bld.Append(this[i]); + if (i < Size - 1) + { + bld.Append(','); + } + } + + bld.Append(']'); + return bld.ToString(); + } + + /// + public bool Equals(Block8x8 other) + { + for (int i = 0; i < Size; i++) + { + if (this[i] != other[i]) + { + return false; + } + } + + return true; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is Block8x8 && this.Equals((Block8x8)obj); + } + + /// + public override int GetHashCode() + { + return (this[0] * 31) + this[1]; + } + + /// + /// Calculate the total sum of absoulute differences of elements in 'a' and 'b'. + /// + public static long TotalDifference(ref Block8x8 a, ref Block8x8 b) + { + long result = 0; + for (int i = 0; i < Size; i++) + { + int d = a[i] - b[i]; + result += Math.Abs(d); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs similarity index 71% rename from src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs rename to src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs index f84dc977f..4c1b4f4d1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming + +using System.Numerics; +using System.Runtime.CompilerServices; + // -#pragma warning disable -namespace ImageSharp.Formats.Jpg +namespace SixLabors.ImageSharp.Formats.Jpeg.Common { - using System.Numerics; - using System.Runtime.CompilerServices; - internal partial struct Block8x8F { private static readonly Vector4 CMin4 = new Vector4(0F); @@ -101,7 +98,7 @@ namespace ImageSharp.Formats.Jpg /// /// The destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) + internal void NormalizeColorsInto(ref Block8x8F d) { d.V0L = Vector4.Clamp(V0L + COff4, CMin4, CMax4); d.V0R = Vector4.Clamp(V0R + COff4, CMin4, CMax4); @@ -120,5 +117,30 @@ namespace ImageSharp.Formats.Jpg d.V7L = Vector4.Clamp(V7L + COff4, CMin4, CMax4); d.V7R = Vector4.Clamp(V7R + COff4, CMin4, CMax4); } - } + + + /// + /// Level shift by +128, clip to [0, 255] + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void NormalizeColorsInplace() + { + this.V0L = Vector4.Clamp(V0L + COff4, CMin4, CMax4); + this.V0R = Vector4.Clamp(V0R + COff4, CMin4, CMax4); + this.V1L = Vector4.Clamp(V1L + COff4, CMin4, CMax4); + this.V1R = Vector4.Clamp(V1R + COff4, CMin4, CMax4); + this.V2L = Vector4.Clamp(V2L + COff4, CMin4, CMax4); + this.V2R = Vector4.Clamp(V2R + COff4, CMin4, CMax4); + this.V3L = Vector4.Clamp(V3L + COff4, CMin4, CMax4); + this.V3R = Vector4.Clamp(V3R + COff4, CMin4, CMax4); + this.V4L = Vector4.Clamp(V4L + COff4, CMin4, CMax4); + this.V4R = Vector4.Clamp(V4R + COff4, CMin4, CMax4); + this.V5L = Vector4.Clamp(V5L + COff4, CMin4, CMax4); + this.V5R = Vector4.Clamp(V5R + COff4, CMin4, CMax4); + this.V6L = Vector4.Clamp(V6L + COff4, CMin4, CMax4); + this.V6R = Vector4.Clamp(V6R + COff4, CMin4, CMax4); + this.V7L = Vector4.Clamp(V7L + COff4, CMin4, CMax4); + this.V7R = Vector4.Clamp(V7R + COff4, CMin4, CMax4); + } + } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt similarity index 87% rename from src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt rename to src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt index 03566acbb..bef3e4914 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.Generated.tt @@ -1,24 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +<# +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming +#> <#@ 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) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + // -#pragma warning disable <# char[] coordz = {'X', 'Y', 'Z', 'W'}; #> -namespace ImageSharp.Formats.Jpg +namespace SixLabors.ImageSharp.Formats.Jpeg.Common { - using System.Numerics; - using System.Runtime.CompilerServices; - internal partial struct Block8x8F { private static readonly Vector4 CMin4 = new Vector4(0F); diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs new file mode 100644 index 000000000..191fa50ca --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs @@ -0,0 +1,688 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using SixLabors.ImageSharp.Memory; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + /// + /// Represents a Jpeg block with coefficients. + /// + internal partial struct Block8x8F + { + // Most of the static methods of this struct are instance methods by actual semantics: they use Block8x8F* as their first parameter. + // Example: GetScalarAt() and SetScalarAt() are really just other (optimized) versions of the indexer. + // It's much cleaner, easier and safer to work with the code, if the methods with same semantics are next to each other. +#pragma warning disable SA1204 // StaticElementsMustAppearBeforeInstanceElements + + /// + /// Vector count + /// + public const int VectorCount = 16; + + /// + /// A number of scalar coefficients in a + /// + public const int Size = 64; + +#pragma warning disable SA1600 // ElementsMustBeDocumented + public Vector4 V0L; + public Vector4 V0R; + + public Vector4 V1L; + public Vector4 V1R; + + public Vector4 V2L; + public Vector4 V2R; + + public Vector4 V3L; + public Vector4 V3R; + + public Vector4 V4L; + public Vector4 V4R; + + public Vector4 V5L; + public Vector4 V5R; + + public Vector4 V6L; + public Vector4 V6R; + + public Vector4 V7L; + public Vector4 V7R; +#pragma warning restore SA1600 // ElementsMustBeDocumented + + private static readonly Vector4 NegativeOne = new Vector4(-1); + private static readonly Vector4 Offset = new Vector4(.5F); + + /// + /// Get/Set scalar elements at a given index + /// + /// The index + /// The float value at the specified index + public float this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + GuardBlockIndex(idx); + ref float selfRef = ref Unsafe.As(ref this); + return Unsafe.Add(ref selfRef, idx); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + GuardBlockIndex(idx); + ref float selfRef = ref Unsafe.As(ref this); + Unsafe.Add(ref selfRef, idx) = value; + } + } + + public float this[int x, int y] + { + get => this[(y * 8) + x]; + set => this[(y * 8) + x] = value; + } + + public static Block8x8F operator *(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val *= value; + result[i] = val; + } + + return result; + } + + public static Block8x8F operator /(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val /= value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F operator +(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val += value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F operator -(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val -= value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F Load(Span data) + { + var result = default(Block8x8F); + result.LoadFrom(data); + return result; + } + + public static Block8x8F Load(Span data) + { + var result = default(Block8x8F); + result.LoadFrom(data); + return result; + } + + /// + /// Pointer-based "Indexer" (getter part) + /// + /// Block pointer + /// Index + /// The scaleVec value at the specified index + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) + { + GuardBlockIndex(idx); + + float* fp = (float*)blockPtr; + return fp[idx]; + } + + /// + /// Pointer-based "Indexer" (setter part) + /// + /// Block pointer + /// Index + /// Value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) + { + GuardBlockIndex(idx); + + float* fp = (float*)blockPtr; + fp[idx] = value; + } + + /// + /// Fill the block with defaults (zeroes) + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + // The cheapest way to do this in C#: + this = default(Block8x8F); + } + + /// + /// Load raw 32bit floating point data from source + /// + /// Source + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void LoadFrom(Span source) + { + ref byte s = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte d = ref Unsafe.As(ref this); + + Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); + } + + /// + /// Load raw 32bit floating point data from source + /// + /// Block pointer + /// Source + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source) + { + blockPtr->LoadFrom(source); + } + + /// + /// Load raw 32bit floating point data from source + /// + /// Source + public unsafe void LoadFrom(Span source) + { + fixed (Vector4* ptr = &this.V0L) + { + float* fp = (float*)ptr; + for (int i = 0; i < Size; i++) + { + fp[i] = source[i]; + } + } + } + + /// + /// Copy raw 32bit floating point data to dest + /// + /// Destination + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyTo(Span dest) + { + ref byte d = ref Unsafe.As(ref dest.DangerousGetPinnableReference()); + ref byte s = ref Unsafe.As(ref this); + + Unsafe.CopyBlock(ref d, ref s, Size * sizeof(float)); + } + + /// + /// Convert salars to byte-s and copy to dest + /// + /// Pointer to block + /// Destination + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) + { + float* fPtr = (float*)blockPtr; + for (int i = 0; i < Size; i++) + { + dest[i] = (byte)*fPtr; + fPtr++; + } + } + + /// + /// Copy raw 32bit floating point data to dest + /// + /// Block pointer + /// Destination + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) + { + blockPtr->CopyTo(dest); + } + + /// + /// Copy raw 32bit floating point data to dest + /// + /// Destination + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyTo(float[] dest) + { + fixed (void* ptr = &this.V0L) + { + Marshal.Copy((IntPtr)ptr, dest, 0, Size); + } + } + + /// + /// Copy raw 32bit floating point data to dest + /// + /// Destination + public unsafe void CopyTo(Span dest) + { + fixed (Vector4* ptr = &this.V0L) + { + float* fp = (float*)ptr; + for (int i = 0; i < Size; i++) + { + dest[i] = (int)fp[i]; + } + } + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyTo(BufferArea area) + { + ref byte selfBase = ref Unsafe.As(ref this); + ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigo()); + int destStride = area.Stride * sizeof(float); + + CopyRowImpl(ref selfBase, ref destBase, destStride, 0); + CopyRowImpl(ref selfBase, ref destBase, destStride, 1); + CopyRowImpl(ref selfBase, ref destBase, destStride, 2); + CopyRowImpl(ref selfBase, ref destBase, destStride, 3); + CopyRowImpl(ref selfBase, ref destBase, destStride, 4); + CopyRowImpl(ref selfBase, ref destBase, destStride, 5); + CopyRowImpl(ref selfBase, ref destBase, destStride, 6); + CopyRowImpl(ref selfBase, ref destBase, destStride, 7); + } + + public void CopyTo(BufferArea area, int horizontalScale, int verticalScale) + { + if (horizontalScale == 1 && verticalScale == 1) + { + this.CopyTo(area); + return; + } + else if (horizontalScale == 2 && verticalScale == 2) + { + this.CopyTo2x2(area); + return; + } + + // TODO: Optimize: implement all the cases with loopless special code! (T4?) + for (int y = 0; y < 8; y++) + { + int yy = y * verticalScale; + int y8 = y * 8; + + for (int x = 0; x < 8; x++) + { + int xx = x * horizontalScale; + + float value = this[y8 + x]; + + for (int i = 0; i < verticalScale; i++) + { + for (int j = 0; j < horizontalScale; j++) + { + area[xx + j, yy + i] = value; + } + } + } + } + } + + private void CopyTo2x2(BufferArea area) + { + ref float destBase = ref area.GetReferenceToOrigo(); + int destStride = area.Stride; + + this.CopyRow2x2Impl(ref destBase, 0, destStride); + this.CopyRow2x2Impl(ref destBase, 1, destStride); + this.CopyRow2x2Impl(ref destBase, 2, destStride); + this.CopyRow2x2Impl(ref destBase, 3, destStride); + this.CopyRow2x2Impl(ref destBase, 4, destStride); + this.CopyRow2x2Impl(ref destBase, 5, destStride); + this.CopyRow2x2Impl(ref destBase, 6, destStride); + this.CopyRow2x2Impl(ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) + { + ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); + ref byte d = ref Unsafe.Add(ref destBase, row * destStride); + Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CopyRow2x2Impl(ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Stride2VectorCopyImpl(ref selfLeft, ref destLocalOrigo); + Stride2VectorCopyImpl(ref selfRight, ref Unsafe.Add(ref destLocalOrigo, 8)); + + Stride2VectorCopyImpl(ref selfLeft, ref Unsafe.Add(ref destLocalOrigo, destStride)); + Stride2VectorCopyImpl(ref selfRight, ref Unsafe.Add(ref destLocalOrigo, destStride + 8)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Stride2VectorCopyImpl(ref Vector4 s, ref float destBase) + { + Unsafe.Add(ref destBase, 0) = s.X; + Unsafe.Add(ref destBase, 1) = s.X; + Unsafe.Add(ref destBase, 2) = s.Y; + Unsafe.Add(ref destBase, 3) = s.Y; + Unsafe.Add(ref destBase, 4) = s.Z; + Unsafe.Add(ref destBase, 5) = s.Z; + Unsafe.Add(ref destBase, 6) = s.W; + Unsafe.Add(ref destBase, 7) = s.W; + } + + public float[] ToArray() + { + float[] result = new float[Size]; + this.CopyTo(result); + return result; + } + + /// + /// Multiply all elements of the block. + /// + /// Vector to multiply by + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MultiplyAllInplace(float scaleVec) + { + this.V0L *= scaleVec; + this.V0R *= scaleVec; + this.V1L *= scaleVec; + this.V1R *= scaleVec; + this.V2L *= scaleVec; + this.V2R *= scaleVec; + this.V3L *= scaleVec; + this.V3R *= scaleVec; + this.V4L *= scaleVec; + this.V4R *= scaleVec; + this.V5L *= scaleVec; + this.V5R *= scaleVec; + this.V6L *= scaleVec; + this.V6R *= scaleVec; + this.V7L *= scaleVec; + this.V7R *= scaleVec; + } + + /// + /// Adds a vector to all elements of the block. + /// + /// The added vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddToAllInplace(Vector4 diff) + { + this.V0L += diff; + this.V0R += diff; + this.V1L += diff; + this.V1R += diff; + this.V2L += diff; + this.V2R += diff; + this.V3L += diff; + this.V3R += diff; + this.V4L += diff; + this.V4R += diff; + this.V5L += diff; + this.V5R += diff; + this.V6L += diff; + this.V6R += diff; + this.V7L += diff; + this.V7R += diff; + } + + /// + /// Quantize the block. + /// + /// Block pointer + /// Qt pointer + /// Unzig pointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void DequantizeBlock(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) + { + float* b = (float*)blockPtr; + float* qtp = (float*)qtPtr; + for (int zig = 0; zig < Size; zig++) + { + float* unzigPos = b + unzigPtr[zig]; + float val = *unzigPos; + val *= qtp[zig]; + *unzigPos = val; + } + } + + /// + /// Level shift by +128, clip to [0, 255], and write to buffer. + /// + /// Color buffer + /// Stride offset + /// Temp Block pointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyColorsTo(Span destinationBuffer, int stride, Block8x8F* tempBlockPtr) + { + this.NormalizeColorsInto(ref *tempBlockPtr); + ref byte d = ref destinationBuffer.DangerousGetPinnableReference(); + float* src = (float*)tempBlockPtr; + for (int i = 0; i < 8; i++) + { + ref byte dRow = ref Unsafe.Add(ref d, i * stride); + Unsafe.Add(ref dRow, 0) = (byte)src[0]; + Unsafe.Add(ref dRow, 1) = (byte)src[1]; + Unsafe.Add(ref dRow, 2) = (byte)src[2]; + Unsafe.Add(ref dRow, 3) = (byte)src[3]; + Unsafe.Add(ref dRow, 4) = (byte)src[4]; + Unsafe.Add(ref dRow, 5) = (byte)src[5]; + Unsafe.Add(ref dRow, 6) = (byte)src[6]; + Unsafe.Add(ref dRow, 7) = (byte)src[7]; + src += 8; + } + } + + /// + /// Unzig the elements of block into dest, while dividing them by elements of qt and "pre-rounding" the values. + /// To finish the rounding it's enough to (int)-cast these values. + /// + /// Source block + /// Destination block + /// The quantization table + /// Pointer to elements of + public static unsafe void UnzigDivRound( + Block8x8F* block, + Block8x8F* dest, + Block8x8F* qt, + int* unzigPtr) + { + float* s = (float*)block; + float* d = (float*)dest; + + for (int zig = 0; zig < Size; zig++) + { + d[zig] = s[unzigPtr[zig]]; + } + + DivideRoundAll(ref *dest, ref *qt); + } + + /// + /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block. + /// + /// The destination block. + /// The source block. + public static unsafe void Scale16X16To8X8(Block8x8F* destination, Block8x8F* source) + { + float* d = (float*)destination; + for (int i = 0; i < 4; i++) + { + int dstOff = ((i & 2) << 4) | ((i & 1) << 2); + + float* iSource = (float*)(source + i); + + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 4; x++) + { + int j = (16 * y) + (2 * x); + float sum = iSource[j] + iSource[j + 1] + iSource[j + 8] + iSource[j + 9]; + d[(8 * y) + x + dstOff] = (sum + 2) / 4; + } + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) + { + a.V0L = DivideRound(a.V0L, b.V0L); + a.V0R = DivideRound(a.V0R, b.V0R); + a.V1L = DivideRound(a.V1L, b.V1L); + a.V1R = DivideRound(a.V1R, b.V1R); + a.V2L = DivideRound(a.V2L, b.V2L); + a.V2R = DivideRound(a.V2R, b.V2R); + a.V3L = DivideRound(a.V3L, b.V3L); + a.V3R = DivideRound(a.V3R, b.V3R); + a.V4L = DivideRound(a.V4L, b.V4L); + a.V4R = DivideRound(a.V4R, b.V4R); + a.V5L = DivideRound(a.V5L, b.V5L); + a.V5R = DivideRound(a.V5R, b.V5R); + a.V6L = DivideRound(a.V6L, b.V6L); + a.V6R = DivideRound(a.V6R, b.V6R); + a.V7L = DivideRound(a.V7L, b.V7L); + a.V7R = DivideRound(a.V7R, b.V7R); + } + + public void RoundInto(ref Block8x8 dest) + { + for (int i = 0; i < Size; i++) + { + float val = this[i]; + if (val < 0) + { + val -= 0.5f; + } + else + { + val += 0.5f; + } + + dest[i] = (short)val; + } + } + + public Block8x8 RoundAsInt16Block() + { + var result = default(Block8x8); + this.RoundInto(ref result); + return result; + } + + public void RoundInplace() + { + if (Vector.Count == 8 && Vector.Count == 8) + { + ref Vector row0 = ref Unsafe.As>(ref this.V0L); + row0 = row0.FastRound(); + ref Vector row1 = ref Unsafe.As>(ref this.V1L); + row1 = row1.FastRound(); + ref Vector row2 = ref Unsafe.As>(ref this.V2L); + row2 = row2.FastRound(); + ref Vector row3 = ref Unsafe.As>(ref this.V3L); + row3 = row3.FastRound(); + ref Vector row4 = ref Unsafe.As>(ref this.V4L); + row4 = row4.FastRound(); + ref Vector row5 = ref Unsafe.As>(ref this.V5L); + row5 = row5.FastRound(); + ref Vector row6 = ref Unsafe.As>(ref this.V6L); + row6 = row6.FastRound(); + ref Vector row7 = ref Unsafe.As>(ref this.V7L); + row7 = row7.FastRound(); + } + else + { + this.RoundInplaceSlow(); + } + } + + private void RoundInplaceSlow() + { + for (int i = 0; i < Size; i++) + { + this[i] = MathF.Round(this[i]); + } + } + + /// + public override string ToString() + { + var bld = new StringBuilder(); + bld.Append('['); + for (int i = 0; i < Size; i++) + { + bld.Append(this[i]); + if (i < Size - 1) + { + bld.Append(','); + } + } + + bld.Append(']'); + return bld.ToString(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) + { + // sign(dividend) = max(min(dividend, 1), -1) + var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One); + + // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend) + return (dividend / divisor) + (sign * Offset); + } + + [Conditional("DEBUG")] + private static void GuardBlockIndex(int idx) + { + DebugGuard.MustBeLessThan(idx, Size, nameof(idx)); + DebugGuard.MustBeGreaterThanOrEqualTo(idx, 0, nameof(idx)); + } + + [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))] + private struct Row + { + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs new file mode 100644 index 000000000..da97f9e2a --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ComponentUtils.cs @@ -0,0 +1,16 @@ +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Various utilities for . + /// + internal static class ComponentUtils + { + /// + /// Gets a reference to the at the given row and column index from + /// + public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by) + { + return ref component.SpectralBlocks[bx, by]; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs new file mode 100644 index 000000000..4109fc10e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IJpegComponent.cs @@ -0,0 +1,46 @@ +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Common interface to represent raw Jpeg components. + /// + internal interface IJpegComponent + { + /// + /// Gets the component's position in the components array. + /// + int Index { get; } + + /// + /// Gets the number of blocks in this component as + /// + Size SizeInBlocks { get; } + + /// + /// Gets the horizontal and the vertical sampling factor as + /// + Size SamplingFactors { get; } + + /// + /// Gets the divisors needed to apply when calculating colors. + /// + /// https://en.wikipedia.org/wiki/Chroma_subsampling + /// + /// In case of 4:2:0 subsampling the values are: Luma.SubSamplingDivisors = (1,1) Chroma.SubSamplingDivisors = (2,2) + /// + Size SubSamplingDivisors { get; } + + /// + /// Gets the index of the quantization table for this block. + /// + int QuantizationTableIndex { get; } + + /// + /// Gets the storing the "raw" frequency-domain decoded + unzigged blocks. + /// We need to apply IDCT and dequantiazition to transform them into color-space blocks. + /// + Buffer2D SpectralBlocks { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs new file mode 100644 index 000000000..3873656a4 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/IRawJpegData.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// + /// Represents decompressed, unprocessed jpeg data with spectral space -s. + /// + internal interface IRawJpegData : IDisposable + { + /// + /// Gets the image size in pixels. + /// + Size ImageSizeInPixels { get; } + + /// + /// Gets the number of coponents. + /// + int ComponentCount { get; } + + /// + /// Gets the color space + /// + JpegColorSpace ColorSpace { get; } + + /// + /// Gets the components. + /// + IEnumerable Components { get; } + + /// + /// Gets the quantization tables, in zigzag order. + /// + Block8x8F[] QuantizationTables { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs new file mode 100644 index 000000000..4c4701753 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs @@ -0,0 +1,155 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct JpegBlockPostProcessor + { + /// + /// The + /// + private ComputationData data; + + /// + /// Pointers to elements of + /// + private DataPointers pointers; + + /// + /// Initialize the instance on the stack. + /// + /// The instance + public static void Init(JpegBlockPostProcessor* postProcessor) + { + postProcessor->data = ComputationData.Create(); + postProcessor->pointers = new DataPointers(&postProcessor->data); + } + + public void QuantizeAndTransform(IRawJpegData decoder, IJpegComponent component, ref Block8x8 sourceBlock) + { + this.data.SourceBlock = sourceBlock.AsFloatBlock(); + int qtIndex = component.QuantizationTableIndex; + this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; + + Block8x8F* b = this.pointers.SourceBlock; + + Block8x8F.DequantizeBlock(b, this.pointers.QuantiazationTable, this.pointers.Unzig); + + FastFloatingPointDCT.TransformIDCT(ref *b, ref this.data.WorkspaceBlock1, ref this.data.WorkspaceBlock2); + } + + public void ProcessBlockColorsInto( + IRawJpegData decoder, + IJpegComponent component, + ref Block8x8 sourceBlock, + BufferArea destArea) + { + this.QuantizeAndTransform(decoder, component, ref sourceBlock); + + this.data.WorkspaceBlock1.NormalizeColorsInplace(); + + // To conform better to libjpeg we actually NEED TO loose precision here. + // This is because they store blocks as Int16 between all the operations. + // Unfortunately, we need to emulate this to be "more accurate" :( + this.data.WorkspaceBlock1.RoundInplace(); + + Size divs = component.SubSamplingDivisors; + this.data.WorkspaceBlock1.CopyTo(destArea, divs.Width, divs.Height); + } + + /// + /// Holds the "large" data blocks needed for computations. + /// + [StructLayout(LayoutKind.Sequential)] + public struct ComputationData + { + /// + /// Source block + /// + public Block8x8F SourceBlock; + + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F WorkspaceBlock1; + + /// + /// Temporal block 2 to store intermediate and/or final computation results + /// + public Block8x8F WorkspaceBlock2; + + /// + /// The quantization table as + /// + public Block8x8F QuantiazationTable; + + /// + /// The jpeg unzig data + /// + public UnzigData Unzig; + + /// + /// Creates and initializes a new instance + /// + /// The + public static ComputationData Create() + { + var data = default(ComputationData); + data.Unzig = UnzigData.Create(); + return data; + } + } + + /// + /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of + /// + public struct DataPointers + { + /// + /// Pointer to + /// + public Block8x8F* SourceBlock; + + /// + /// Pointer to + /// + public Block8x8F* WorkspaceBlock1; + + /// + /// Pointer to + /// + public Block8x8F* WorkspaceBlock2; + + /// + /// Pointer to + /// + public Block8x8F* QuantiazationTable; + + /// + /// Pointer to as int* + /// + public int* Unzig; + + /// + /// Initializes a new instance of the struct. + /// + /// Pointer to + internal DataPointers(ComputationData* dataPtr) + { + this.SourceBlock = &dataPtr->SourceBlock; + this.WorkspaceBlock1 = &dataPtr->WorkspaceBlock1; + this.WorkspaceBlock2 = &dataPtr->WorkspaceBlock2; + this.QuantiazationTable = &dataPtr->QuantiazationTable; + this.Unzig = dataPtr->Unzig.Data; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromCmyk.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromCmyk.cs new file mode 100644 index 000000000..908ffb85e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromCmyk.cs @@ -0,0 +1,46 @@ +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + internal abstract partial class JpegColorConverter + { + internal class FromCmyk : JpegColorConverter + { + public FromCmyk() + : base(JpegColorSpace.Cmyk) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan cVals = values.Component0; + ReadOnlySpan mVals = values.Component1; + ReadOnlySpan yVals = values.Component2; + ReadOnlySpan kVals = values.Component3; + + var v = new Vector4(0, 0, 0, 1F); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float c = cVals[i]; + float m = mVals[i]; + float y = yVals[i]; + float k = kVals[i] / 255F; + + v.X = c * k; + v.Y = m * k; + v.Z = y * k; + v.W = 1F; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromGrayScale.cs new file mode 100644 index 000000000..2b19eebac --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromGrayScale.cs @@ -0,0 +1,39 @@ +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + internal abstract partial class JpegColorConverter + { + internal class FromGrayScale : JpegColorConverter + { + public FromGrayScale() + : base(JpegColorSpace.GrayScale) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan yVals = values.Component0; + + var v = new Vector4(0, 0, 0, 1); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float y = yVals[i]; + + v.X = y; + v.Y = y; + v.Z = y; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromRgb.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromRgb.cs new file mode 100644 index 000000000..c4a30ea3e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromRgb.cs @@ -0,0 +1,43 @@ +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + internal abstract partial class JpegColorConverter + { + internal class FromRgb : JpegColorConverter + { + public FromRgb() + : base(JpegColorSpace.RGB) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan rVals = values.Component0; + ReadOnlySpan gVals = values.Component1; + ReadOnlySpan bVals = values.Component2; + + var v = new Vector4(0, 0, 0, 1); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float r = rVals[i]; + float g = gVals[i]; + float b = bVals[i]; + + v.X = r; + v.Y = g; + v.Z = b; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYCbCr.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYCbCr.cs new file mode 100644 index 000000000..059b2e89a --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYCbCr.cs @@ -0,0 +1,234 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + internal abstract partial class JpegColorConverter + { + internal class FromYCbCrBasic : JpegColorConverter + { + public FromYCbCrBasic() + : base(JpegColorSpace.YCbCr) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + ConvertCore(values, result); + } + + internal static void ConvertCore(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan yVals = values.Component0; + ReadOnlySpan cbVals = values.Component1; + ReadOnlySpan crVals = values.Component2; + + var v = new Vector4(0, 0, 0, 1); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float y = yVals[i]; + float cb = cbVals[i] - 128F; + float cr = crVals[i] - 128F; + + v.X = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); + v.Y = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); + v.Z = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + + v *= scale; + + result[i] = v; + } + } + } + + internal class FromYCbCrSimd : JpegColorConverter + { + public FromYCbCrSimd() + : base(JpegColorSpace.YCbCr) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + int remainder = result.Length % 8; + int simdCount = result.Length - remainder; + if (simdCount > 0) + { + ConvertCore(values.Slice(0, simdCount), result.Slice(0, simdCount)); + } + + FromYCbCrBasic.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); + } + + /// + /// SIMD convert using buffers of sizes divisable by 8. + /// + internal static void ConvertCore(ComponentValues values, Span result) + { + DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); + + ref Vector4Pair yBase = + ref Unsafe.As(ref values.Component0.DangerousGetPinnableReference()); + ref Vector4Pair cbBase = + ref Unsafe.As(ref values.Component1.DangerousGetPinnableReference()); + ref Vector4Pair crBase = + ref Unsafe.As(ref values.Component2.DangerousGetPinnableReference()); + + ref Vector4Octet resultBase = + ref Unsafe.As(ref result.DangerousGetPinnableReference()); + + var chromaOffset = new Vector4(-128f); + + // Walking 8 elements at one step: + int n = result.Length / 8; + + for (int i = 0; i < n; i++) + { + // y = yVals[i]; + Vector4Pair y = Unsafe.Add(ref yBase, i); + + // cb = cbVals[i] - 128F; + Vector4Pair cb = Unsafe.Add(ref cbBase, i); + cb.AddInplace(chromaOffset); + + // cr = crVals[i] - 128F; + Vector4Pair cr = Unsafe.Add(ref crBase, i); + cr.AddInplace(chromaOffset); + + // r = y + (1.402F * cr); + Vector4Pair r = y; + Vector4Pair tmp = cr; + tmp.MultiplyInplace(1.402F); + r.AddInplace(ref tmp); + + // g = y - (0.344136F * cb) - (0.714136F * cr); + Vector4Pair g = y; + tmp = cb; + tmp.MultiplyInplace(-0.344136F); + g.AddInplace(ref tmp); + tmp = cr; + tmp.MultiplyInplace(-0.714136F); + g.AddInplace(ref tmp); + + // b = y + (1.772F * cb); + Vector4Pair b = y; + tmp = cb; + tmp.MultiplyInplace(1.772F); + b.AddInplace(ref tmp); + + r.RoundAndDownscale(); + g.RoundAndDownscale(); + b.RoundAndDownscale(); + + // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: + ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); + destination.Collect(ref r, ref g, ref b); + } + } + + /// + /// Its faster to process multiple Vector4-s together + /// + private struct Vector4Pair + { + public Vector4 A; + + public Vector4 B; + + private static readonly Vector4 Scale = new Vector4(1 / 255f); + + private static readonly Vector4 Half = new Vector4(0.5f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RoundAndDownscale() + { + // Emulate rounding: + this.A += Half; + this.B += Half; + + // Downscale by 1/255 + this.A *= Scale; + this.B *= Scale; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MultiplyInplace(float value) + { + this.A *= value; + this.B *= value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddInplace(Vector4 value) + { + this.A += value; + this.B += value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddInplace(ref Vector4Pair other) + { + this.A += other.A; + this.B += other.B; + } + } + + private struct Vector4Octet + { +#pragma warning disable SA1132 // Do not combine fields + public Vector4 V0, V1, V2, V3, V4, V5, V6, V7; + + /// + /// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order. + /// + public void Collect(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b) + { + this.V0.X = r.A.X; + this.V0.Y = g.A.X; + this.V0.Z = b.A.X; + this.V0.W = 1f; + + this.V1.X = r.A.Y; + this.V1.Y = g.A.Y; + this.V1.Z = b.A.Y; + this.V1.W = 1f; + + this.V2.X = r.A.Z; + this.V2.Y = g.A.Z; + this.V2.Z = b.A.Z; + this.V2.W = 1f; + + this.V3.X = r.A.W; + this.V3.Y = g.A.W; + this.V3.Z = b.A.W; + this.V3.W = 1f; + + this.V4.X = r.B.X; + this.V4.Y = g.B.X; + this.V4.Z = b.B.X; + this.V4.W = 1f; + + this.V5.X = r.B.Y; + this.V5.Y = g.B.Y; + this.V5.Z = b.B.Y; + this.V5.W = 1f; + + this.V6.X = r.B.Z; + this.V6.Y = g.B.Z; + this.V6.Z = b.B.Z; + this.V6.W = 1f; + + this.V7.X = r.B.W; + this.V7.Y = g.B.W; + this.V7.Z = b.B.W; + this.V7.W = 1f; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYccK.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYccK.cs new file mode 100644 index 000000000..7e6edbdce --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.FromYccK.cs @@ -0,0 +1,46 @@ +using System; +using System.Numerics; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + internal abstract partial class JpegColorConverter + { + internal class FromYccK : JpegColorConverter + { + public FromYccK() + : base(JpegColorSpace.Ycck) + { + } + + public override void ConvertToRGBA(ComponentValues values, Span result) + { + // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! + ReadOnlySpan yVals = values.Component0; + ReadOnlySpan cbVals = values.Component1; + ReadOnlySpan crVals = values.Component2; + ReadOnlySpan kVals = values.Component3; + + var v = new Vector4(0, 0, 0, 1F); + + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + for (int i = 0; i < result.Length; i++) + { + float y = yVals[i]; + float cb = cbVals[i] - 128F; + float cr = crVals[i] - 128F; + float k = kVals[i] / 255F; + + v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; + v.Y = (255F - MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero)) * k; + v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; + v.W = 1F; + + v *= scale; + + result[i] = v; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs new file mode 100644 index 000000000..1c2dda01f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorConverter.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates the conversion of Jpeg channels to RGBA values packed in buffer. + /// + internal abstract partial class JpegColorConverter + { + /// + /// The avalilable converters + /// + private static readonly JpegColorConverter[] Converters = { new FromYCbCrSimd(), new FromYccK(), new FromCmyk(), new FromGrayScale(), new FromRgb() }; + + /// + /// Initializes a new instance of the class. + /// + protected JpegColorConverter(JpegColorSpace colorSpace) + { + this.ColorSpace = colorSpace; + } + + /// + /// Gets the of this converter. + /// + public JpegColorSpace ColorSpace { get; } + + /// + /// Returns the corresponding to the given + /// + public static JpegColorConverter GetConverter(JpegColorSpace colorSpace) + { + JpegColorConverter converter = Converters.FirstOrDefault(c => c.ColorSpace == colorSpace); + if (converter == null) + { + throw new Exception($"Could not find any converter for JpegColorSpace {colorSpace}!"); + } + + return converter; + } + + /// + /// He implementation of the conversion. + /// + /// The input as a stack-only struct + /// The destination buffer of values + public abstract void ConvertToRGBA(ComponentValues values, Span result); + + /// + /// A stack-only struct to reference the input buffers using -s. + /// + public struct ComponentValues + { + /// + /// The component count + /// + public readonly int ComponentCount; + + /// + /// The component 0 (eg. Y) + /// + public readonly ReadOnlySpan Component0; + + /// + /// The component 1 (eg. Cb) + /// + public readonly ReadOnlySpan Component1; + + /// + /// The component 2 (eg. Cr) + /// + public readonly ReadOnlySpan Component2; + + /// + /// The component 4 + /// + public readonly ReadOnlySpan Component3; + + /// + /// Initializes a new instance of the struct. + /// + /// The 1-4 sized list of component buffers. + /// The row to convert + public ComponentValues(IReadOnlyList> componentBuffers, int row) + { + this.ComponentCount = componentBuffers.Count; + + this.Component0 = componentBuffers[0].GetRowSpan(row); + this.Component1 = Span.Empty; + this.Component2 = Span.Empty; + this.Component3 = Span.Empty; + + if (this.ComponentCount > 1) + { + this.Component1 = componentBuffers[1].GetRowSpan(row); + if (this.ComponentCount > 2) + { + this.Component2 = componentBuffers[2].GetRowSpan(row); + if (this.ComponentCount > 3) + { + this.Component3 = componentBuffers[3].GetRowSpan(row); + } + } + } + } + + private ComponentValues( + int componentCount, + ReadOnlySpan c0, + ReadOnlySpan c1, + ReadOnlySpan c2, + ReadOnlySpan c3) + { + this.ComponentCount = componentCount; + this.Component0 = c0; + this.Component1 = c1; + this.Component2 = c2; + this.Component3 = c3; + } + + public ComponentValues Slice(int start, int length) + { + ReadOnlySpan c0 = this.Component0.Slice(start, length); + ReadOnlySpan c1 = this.ComponentCount > 1 ? this.Component1.Slice(start, length) : ReadOnlySpan.Empty; + ReadOnlySpan c2 = this.ComponentCount > 2 ? this.Component2.Slice(start, length) : ReadOnlySpan.Empty; + ReadOnlySpan c3 = this.ComponentCount > 3 ? this.Component3.Slice(start, length) : ReadOnlySpan.Empty; + + return new ComponentValues(this.ComponentCount, c0, c1, c2, c3); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs new file mode 100644 index 000000000..da353d279 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegColorSpace.cs @@ -0,0 +1,20 @@ +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Identifies the colorspace of a Jpeg image + /// + internal enum JpegColorSpace + { + Undefined = 0, + + GrayScale, + + Ycck, + + Cmyk, + + RGB, + + YCbCr + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs new file mode 100644 index 000000000..feb5164d7 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegComponentPostProcessor.cs @@ -0,0 +1,105 @@ +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates postprocessing data for one component for . + /// + internal class JpegComponentPostProcessor : IDisposable + { + /// + /// Points to the current row in . + /// + private int currentComponentRowInBlocks; + + /// + /// The size of the area in corrsponding to one 8x8 Jpeg block + /// + private readonly Size blockAreaSize; + + /// + /// Initializes a new instance of the class. + /// + public JpegComponentPostProcessor(JpegImagePostProcessor imagePostProcessor, IJpegComponent component) + { + this.Component = component; + this.ImagePostProcessor = imagePostProcessor; + this.ColorBuffer = new Buffer2D(imagePostProcessor.PostProcessorBufferSize); + + this.BlockRowsPerStep = JpegImagePostProcessor.BlockRowsPerStep / this.Component.SubSamplingDivisors.Height; + this.blockAreaSize = this.Component.SubSamplingDivisors * 8; + } + + /// + /// Gets the + /// + public JpegImagePostProcessor ImagePostProcessor { get; } + + /// + /// Gets the + /// + public IJpegComponent Component { get; } + + /// + /// Gets the temporal working buffer of color values. + /// + public Buffer2D ColorBuffer { get; } + + /// + /// Gets + /// + public Size SizeInBlocks => this.Component.SizeInBlocks; + + /// + /// Gets the maximal number of block rows being processed in one step. + /// + public int BlockRowsPerStep { get; } + + /// + public void Dispose() + { + this.ColorBuffer.Dispose(); + } + + /// + /// Invoke for block rows, copy the result into . + /// + public unsafe void CopyBlocksToColorBuffer() + { + var blockPp = default(JpegBlockPostProcessor); + JpegBlockPostProcessor.Init(&blockPp); + + for (int y = 0; y < this.BlockRowsPerStep; y++) + { + int yBlock = this.currentComponentRowInBlocks + y; + + if (yBlock >= this.SizeInBlocks.Height) + { + break; + } + + int yBuffer = y * this.blockAreaSize.Height; + + for (int x = 0; x < this.SizeInBlocks.Width; x++) + { + int xBlock = x; + int xBuffer = x * this.blockAreaSize.Width; + + ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock); + + BufferArea destArea = this.ColorBuffer.GetArea( + xBuffer, + yBuffer, + this.blockAreaSize.Width, + this.blockAreaSize.Height); + + blockPp.ProcessBlockColorsInto(this.ImagePostProcessor.RawJpeg, this.Component, ref block, destArea); + } + } + + this.currentComponentRowInBlocks += this.BlockRowsPerStep; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs new file mode 100644 index 000000000..84867d276 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegImagePostProcessor.cs @@ -0,0 +1,160 @@ +using System; +using System.Linq; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Encapsulates the execution od post-processing algorithms to be applied on a to produce a valid :
+ /// (1) Dequantization
+ /// (2) IDCT
+ /// (3) Color conversion form one of the -s into a buffer of RGBA values
+ /// (4) Packing pixels from the buffer.
+ /// These operations are executed in steps. + /// image rows are converted in one step, + /// which means that size of the allocated memory is limited (does not depend on ). + ///
+ internal class JpegImagePostProcessor : IDisposable + { + /// + /// The number of block rows to be processed in one Step. + /// + public const int BlockRowsPerStep = 4; + + /// + /// The number of image pixel rows to be processed in one step. + /// + public const int PixelRowsPerStep = 4 * 8; + + /// + /// Temporal buffer to store a row of colors. + /// + private readonly Buffer rgbaBuffer; + + /// + /// The corresponding to the current determined by . + /// + private JpegColorConverter colorConverter; + + /// + /// Initializes a new instance of the class. + /// + /// The representing the uncompressed spectral Jpeg data + public JpegImagePostProcessor(IRawJpegData rawJpeg) + { + this.RawJpeg = rawJpeg; + IJpegComponent c0 = rawJpeg.Components.First(); + this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; + this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); + + this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(this, c)).ToArray(); + this.rgbaBuffer = new Buffer(rawJpeg.ImageSizeInPixels.Width); + this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); + } + + /// + /// Gets the instances. + /// + public JpegComponentPostProcessor[] ComponentProcessors { get; } + + /// + /// Gets the to be processed. + /// + public IRawJpegData RawJpeg { get; } + + /// + /// Gets the total number of post processor steps deduced from the height of the image and . + /// + public int NumberOfPostProcessorSteps { get; } + + /// + /// Gets the size of the temporal buffers we need to allocate into . + /// + public Size PostProcessorBufferSize { get; } + + /// + /// Gets the value of the counter that grows by each step by . + /// + public int PixelRowCounter { get; private set; } + + /// + public void Dispose() + { + foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) + { + cpp.Dispose(); + } + + this.rgbaBuffer.Dispose(); + } + + /// + /// Process all pixels into 'destination'. The image dimensions should match . + /// + /// The pixel type + /// The destination image + public void PostProcess(ImageFrame destination) + where TPixel : struct, IPixel + { + this.PixelRowCounter = 0; + + if (this.RawJpeg.ImageSizeInPixels != destination.Size()) + { + throw new ArgumentException("Input image is not of the size of the processed one!"); + } + + while (this.PixelRowCounter < this.RawJpeg.ImageSizeInPixels.Height) + { + this.DoPostProcessorStep(destination); + } + } + + /// + /// Execute one step rocessing pixel rows into 'destination'. + /// + /// The pixel type + /// The destination image. + public void DoPostProcessorStep(ImageFrame destination) + where TPixel : struct, IPixel + { + foreach (JpegComponentPostProcessor cpp in this.ComponentProcessors) + { + cpp.CopyBlocksToColorBuffer(); + } + + this.ConvertColorsInto(destination); + + this.PixelRowCounter += PixelRowsPerStep; + } + + /// + /// Convert and copy row of colors into 'destination' starting at row . + /// + /// The pixel type + /// The destination image + private void ConvertColorsInto(ImageFrame destination) + where TPixel : struct, IPixel + { + int maxY = Math.Min(destination.Height, this.PixelRowCounter + PixelRowsPerStep); + + Buffer2D[] buffers = this.ComponentProcessors.Select(cp => cp.ColorBuffer).ToArray(); + + for (int yy = this.PixelRowCounter; yy < maxY; yy++) + { + int y = yy - this.PixelRowCounter; + + var values = new JpegColorConverter.ComponentValues(buffers, y); + this.colorConverter.ConvertToRGBA(values, this.rgbaBuffer); + + Span destRow = destination.GetPixelRowSpan(yy); + + PixelOperations.Instance.PackFromVector4(this.rgbaBuffer, destRow, destination.Width); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/DCT.cs b/src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs similarity index 88% rename from src/ImageSharp/Formats/Jpeg/Components/DCT.cs rename to src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs index 5729fe46d..8a4f56e3d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/DCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/FastFloatingPointDCT.cs @@ -1,50 +1,49 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpg -{ - using System.Numerics; - using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ /// - /// Contains forward and inverse DCT implementations + /// Contains inaccurate, but fast forward and inverse DCT implementations. /// - internal static class DCT + internal static class FastFloatingPointDCT { #pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore - private static readonly float C_1_175876 = 1.175876f; + private const float C_1_175876 = 1.175875602f; - private static readonly float C_1_961571 = -1.961571f; + private const float C_1_961571 = -1.961570560f; - private static readonly float C_0_390181 = -0.390181f; + private const float C_0_390181 = -0.390180644f; - private static readonly float C_0_899976 = -0.899976f; + private const float C_0_899976 = -0.899976223f; - private static readonly float C_2_562915 = -2.562915f; + private const float C_2_562915 = -2.562915447f; - private static readonly float C_0_298631 = 0.298631f; + private const float C_0_298631 = 0.298631336f; - private static readonly float C_2_053120 = 2.053120f; + private const float C_2_053120 = 2.053119869f; - private static readonly float C_3_072711 = 3.072711f; + private const float C_3_072711 = 3.072711026f; - private static readonly float C_1_501321 = 1.501321f; + private const float C_1_501321 = 1.501321110f; - private static readonly float C_0_541196 = 0.541196f; + private const float C_0_541196 = 0.541196100f; - private static readonly float C_1_847759 = -1.847759f; + private const float C_1_847759 = -1.847759065f; - private static readonly float C_0_765367 = 0.765367f; + private const float C_0_765367 = 0.765366865f; - private static readonly float C_0_125 = 0.1250f; + private const float C_0_125 = 0.1250f; #pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); /// - /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization) + /// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization). + /// Ported from https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 /// /// Source /// Destination @@ -60,6 +59,7 @@ namespace ImageSharp.Formats.Jpg IDCT8x4_LeftPart(ref temp, ref dest); IDCT8x4_RightPart(ref temp, ref dest); + // TODO: What if we leave the blocks in a scaled-by-x8 state until final color packing? dest.MultiplyAllInplace(C_0_125); } diff --git a/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs new file mode 100644 index 000000000..b9bfe425a --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/SizeExtensions.cs @@ -0,0 +1,48 @@ +using System.Numerics; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ + /// + /// Extension methods for + /// + internal static class SizeExtensions + { + /// + /// Multiplies 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'. + /// TODO: Shouldn't we expose this as operator in SixLabors.Core? + /// + public static Size MultiplyBy(this Size a, Size b) => new Size(a.Width * b.Width, a.Height * b.Height); + + /// + /// Divides 'a.Width' with 'b.Width' and 'a.Height' with 'b.Height'. + /// TODO: Shouldn't we expose this as operator in SixLabors.Core? + /// + public static Size DivideBy(this Size a, Size b) => new Size(a.Width / b.Width, a.Height / b.Height); + + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// + public static Size DivideRoundUp(this Size originalSize, int divX, int divY) + { + var sizeVect = (Vector2)(SizeF)originalSize; + sizeVect /= new Vector2(divX, divY); + sizeVect.X = MathF.Ceiling(sizeVect.X); + sizeVect.Y = MathF.Ceiling(sizeVect.Y); + + return new Size((int)sizeVect.X, (int)sizeVect.Y); + } + + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// + public static Size DivideRoundUp(this Size originalSize, int divisor) => + DivideRoundUp(originalSize, divisor, divisor); + + /// + /// Divide Width and Height as real numbers and return the Ceiling. + /// + public static Size DivideRoundUp(this Size originalSize, Size divisor) => + DivideRoundUp(originalSize, divisor.Width, divisor.Height); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/UnzigData.cs b/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs similarity index 88% rename from src/ImageSharp/Formats/Jpeg/UnzigData.cs rename to src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs index e74dd5c73..e243938e3 100644 --- a/src/ImageSharp/Formats/Jpeg/UnzigData.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.Common +{ /// + /// TODO: This should be simply just a ! /// Holds the Jpeg UnZig array in a value/stack type. /// Unzig maps from the zigzag ordering to the natural ordering. For example, /// unzig[3] is the column and row of the fourth element in zigzag order. The diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs deleted file mode 100644 index 130b5856c..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ /dev/null @@ -1,415 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// DCT code Ported from https://github.com/norishigefukushima/dct_simd - /// - internal partial struct Block8x8F - { - // Most of the static methods of this struct are instance methods by actual semantics: they use Block8x8F* as their first parameter. - // Example: GetScalarAt() and SetScalarAt() are really just other (optimized) versions of the indexer. - // It's much cleaner, easier and safer to work with the code, if the methods with same semantics are next to each other. -#pragma warning disable SA1204 // StaticElementsMustAppearBeforeInstanceElements - - /// - /// Vector count - /// - public const int VectorCount = 16; - - /// - /// Scalar count - /// - public const int ScalarCount = VectorCount * 4; - -#pragma warning disable SA1600 // ElementsMustBeDocumented - public Vector4 V0L; - public Vector4 V0R; - - public Vector4 V1L; - public Vector4 V1R; - - public Vector4 V2L; - public Vector4 V2R; - - public Vector4 V3L; - public Vector4 V3R; - - public Vector4 V4L; - public Vector4 V4R; - - public Vector4 V5L; - public Vector4 V5R; - - public Vector4 V6L; - public Vector4 V6R; - - public Vector4 V7L; - public Vector4 V7R; -#pragma warning restore SA1600 // ElementsMustBeDocumented - - private static readonly Vector4 NegativeOne = new Vector4(-1); - private static readonly Vector4 Offset = new Vector4(.5F); - - /// - /// Get/Set scalar elements at a given index - /// - /// The index - /// The float value at the specified index - public unsafe float this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - fixed (Block8x8F* p = &this) - { - float* fp = (float*)p; - return fp[idx]; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - fixed (Block8x8F* p = &this) - { - float* fp = (float*)p; - fp[idx] = value; - } - } - } - - /// - /// Pointer-based "Indexer" (getter part) - /// - /// Block pointer - /// Index - /// The scaleVec value at the specified index - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) - { - float* fp = (float*)blockPtr; - return fp[idx]; - } - - /// - /// Pointer-based "Indexer" (setter part) - /// - /// Block pointer - /// Index - /// Value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) - { - float* fp = (float*)blockPtr; - fp[idx] = value; - } - - /// - /// Fill the block with defaults (zeroes) - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() - { - // The cheapest way to do this in C#: - this = default(Block8x8F); - } - - /// - /// Load raw 32bit floating point data from source - /// - /// Source - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void LoadFrom(MutableSpan source) - { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount); - } - } - - /// - /// Load raw 32bit floating point data from source - /// - /// Block pointer - /// Source - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan source) - { - Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount); - } - - /// - /// Load raw 32bit floating point data from source - /// - /// Source - public unsafe void LoadFrom(MutableSpan source) - { - fixed (Vector4* ptr = &this.V0L) - { - float* fp = (float*)ptr; - for (int i = 0; i < ScalarCount; i++) - { - fp[i] = source[i]; - } - } - } - - /// - /// Copy raw 32bit floating point data to dest - /// - /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(MutableSpan dest) - { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount); - } - } - - /// - /// Convert salars to byte-s and copy to dest - /// - /// Pointer to block - /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan dest) - { - float* fPtr = (float*)blockPtr; - for (int i = 0; i < ScalarCount; i++) - { - dest[i] = (byte)*fPtr; - fPtr++; - } - } - - /// - /// Copy raw 32bit floating point data to dest - /// - /// Block pointer - /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan dest) - { - Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount); - } - - /// - /// Copy raw 32bit floating point data to dest - /// - /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(float[] dest) - { - fixed (void* ptr = &this.V0L) - { - Marshal.Copy((IntPtr)ptr, dest, 0, ScalarCount); - } - } - - /// - /// Copy raw 32bit floating point data to dest - /// - /// Destination - public unsafe void CopyTo(MutableSpan dest) - { - fixed (Vector4* ptr = &this.V0L) - { - float* fp = (float*)ptr; - for (int i = 0; i < ScalarCount; i++) - { - dest[i] = (int)fp[i]; - } - } - } - - /// - /// Multiply all elements of the block. - /// - /// Vector to multiply by - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void MultiplyAllInplace(float scaleVec) - { - this.V0L *= scaleVec; - this.V0R *= scaleVec; - this.V1L *= scaleVec; - this.V1R *= scaleVec; - this.V2L *= scaleVec; - this.V2R *= scaleVec; - this.V3L *= scaleVec; - this.V3R *= scaleVec; - this.V4L *= scaleVec; - this.V4R *= scaleVec; - this.V5L *= scaleVec; - this.V5R *= scaleVec; - this.V6L *= scaleVec; - this.V6R *= scaleVec; - this.V7L *= scaleVec; - this.V7R *= scaleVec; - } - - /// - /// Adds a vector to all elements of the block. - /// - /// The added vector - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddToAllInplace(Vector4 diff) - { - this.V0L += diff; - this.V0R += diff; - this.V1L += diff; - this.V1R += diff; - this.V2L += diff; - this.V2R += diff; - this.V3L += diff; - this.V3R += diff; - this.V4L += diff; - this.V4R += diff; - this.V5L += diff; - this.V5R += diff; - this.V6L += diff; - this.V6R += diff; - this.V7L += diff; - this.V7R += diff; - } - - /// - /// Un-zig - /// - /// Block pointer - /// Qt pointer - /// Unzig pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void UnZig(Block8x8F* blockPtr, Block8x8F* qtPtr, int* unzigPtr) - { - float* b = (float*)blockPtr; - float* qtp = (float*)qtPtr; - for (int zig = 0; zig < ScalarCount; zig++) - { - float* unzigPos = b + unzigPtr[zig]; - float val = *unzigPos; - val *= qtp[zig]; - *unzigPos = val; - } - } - - /// - /// Level shift by +128, clip to [0, 255], and write to buffer. - /// - /// Color buffer - /// Stride offset - /// Temp Block pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyColorsTo(MutableSpan buffer, int stride, Block8x8F* tempBlockPtr) - { - this.TransformByteConvetibleColorValuesInto(ref *tempBlockPtr); - - float* src = (float*)tempBlockPtr; - for (int i = 0; i < 8; i++) - { - buffer[0] = (byte)src[0]; - buffer[1] = (byte)src[1]; - buffer[2] = (byte)src[2]; - buffer[3] = (byte)src[3]; - buffer[4] = (byte)src[4]; - buffer[5] = (byte)src[5]; - buffer[6] = (byte)src[6]; - buffer[7] = (byte)src[7]; - buffer.AddOffset(stride); - src += 8; - } - } - - /// - /// Unzig the elements of block into dest, while dividing them by elements of qt and "pre-rounding" the values. - /// To finish the rounding it's enough to (int)-cast these values. - /// - /// Source block - /// Destination block - /// The quantization table - /// Pointer to elements of - public static unsafe void UnzigDivRound( - Block8x8F* block, - Block8x8F* dest, - Block8x8F* qt, - int* unzigPtr) - { - float* s = (float*)block; - float* d = (float*)dest; - - for (int zig = 0; zig < ScalarCount; zig++) - { - d[zig] = s[unzigPtr[zig]]; - } - - DivideRoundAll(ref *dest, ref *qt); - } - - /// - /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block. - /// - /// The destination block. - /// The source block. - public static unsafe void Scale16X16To8X8(Block8x8F* destination, Block8x8F* source) - { - float* d = (float*)destination; - for (int i = 0; i < 4; i++) - { - int dstOff = ((i & 2) << 4) | ((i & 1) << 2); - - float* iSource = (float*)(source + i); - - for (int y = 0; y < 4; y++) - { - for (int x = 0; x < 4; x++) - { - int j = (16 * y) + (2 * x); - float sum = iSource[j] + iSource[j + 1] + iSource[j + 8] + iSource[j + 9]; - d[(8 * y) + x + dstOff] = (sum + 2) / 4; - } - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) - { - a.V0L = DivideRound(a.V0L, b.V0L); - a.V0R = DivideRound(a.V0R, b.V0R); - a.V1L = DivideRound(a.V1L, b.V1L); - a.V1R = DivideRound(a.V1R, b.V1R); - a.V2L = DivideRound(a.V2L, b.V2L); - a.V2R = DivideRound(a.V2R, b.V2R); - a.V3L = DivideRound(a.V3L, b.V3L); - a.V3R = DivideRound(a.V3R, b.V3R); - a.V4L = DivideRound(a.V4L, b.V4L); - a.V4R = DivideRound(a.V4R, b.V4R); - a.V5L = DivideRound(a.V5L, b.V5L); - a.V5R = DivideRound(a.V5R, b.V5R); - a.V6L = DivideRound(a.V6L, b.V6L); - a.V6R = DivideRound(a.V6R, b.V6R); - a.V7L = DivideRound(a.V7L, b.V7L); - a.V7R = DivideRound(a.V7R, b.V7R); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) - { - // sign(dividend) = max(min(dividend, 1), -1) - var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One); - - // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend) - return (dividend / divisor) + (sign * Offset); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Component.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/Component.cs deleted file mode 100644 index 5b53db190..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Component.cs +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - /// - /// Represents a single color component - /// - internal struct Component - { - /// - /// Gets or sets the horizontal sampling factor. - /// - public int HorizontalFactor; - - /// - /// Gets or sets the vertical sampling factor. - /// - public int VerticalFactor; - - /// - /// Gets or sets the identifier - /// - public byte Identifier; - - /// - /// Gets or sets the quantization table destination selector. - /// - public byte Selector; - } -} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlock.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlock.cs deleted file mode 100644 index 900d77ec4..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlock.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System; - - /// - /// A structure to store unprocessed instances and their coordinates while scanning the image. - /// The is present in a "raw" decoded frequency-domain form. - /// We need to apply IDCT and unzigging to transform them into color-space blocks. - /// - internal struct DecodedBlock - { - /// - /// A value indicating whether the instance is initialized. - /// - public bool Initialized; - - /// - /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - public int Bx; - - /// - /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - public int By; - - /// - /// The - /// - public Block8x8F Block; - - /// - /// Store the block data into a - /// - /// X coordinate of the block - /// Y coordinate of the block - /// The - public void SaveBlock(int bx, int by, ref Block8x8F block) - { - this.Bx = bx; - this.By = by; - this.Block = block; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs deleted file mode 100644 index 71472c00f..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs +++ /dev/null @@ -1,168 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using ImageSharp.Memory; - - /// - /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. - /// - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct JpegBlockProcessor - { - /// - /// The - /// - private ComputationData data; - - /// - /// Pointers to elements of - /// - private DataPointers pointers; - - /// - /// The component index. - /// - private int componentIndex; - - /// - /// Initialize the instance on the stack. - /// - /// The instance - /// The current component index - public static void Init(JpegBlockProcessor* processor, int componentIndex) - { - processor->componentIndex = componentIndex; - processor->data = ComputationData.Create(); - processor->pointers = new DataPointers(&processor->data); - } - - /// - /// Dequantize, perform the inverse DCT and store the blocks to the into the corresponding instances. - /// - /// The instance - public void ProcessAllBlocks(JpegDecoderCore decoder) - { - Buffer blockArray = decoder.DecodedBlocks[this.componentIndex]; - for (int i = 0; i < blockArray.Length; i++) - { - this.ProcessBlockColors(decoder, ref blockArray[i]); - } - } - - /// - /// Dequantize, perform the inverse DCT and store decodedBlock.Block to the into the corresponding instance. - /// - /// The - /// The - private void ProcessBlockColors(JpegDecoderCore decoder, ref DecodedBlock decodedBlock) - { - this.data.Block = decodedBlock.Block; - int qtIndex = decoder.ComponentArray[this.componentIndex].Selector; - this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; - - Block8x8F* b = this.pointers.Block; - - Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); - - DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); - - JpegPixelArea destChannel = decoder.GetDestinationChannel(this.componentIndex); - JpegPixelArea destArea = destChannel.GetOffsetedSubAreaForBlock(decodedBlock.Bx, decodedBlock.By); - destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); - } - - /// - /// Holds the "large" data blocks needed for computations. - /// - [StructLayout(LayoutKind.Sequential)] - public struct ComputationData - { - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Block; - - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Temp1; - - /// - /// Temporal block 2 to store intermediate and/or final computation results - /// - public Block8x8F Temp2; - - /// - /// The quantization table as - /// - public Block8x8F QuantiazationTable; - - /// - /// The jpeg unzig data - /// - public UnzigData Unzig; - - /// - /// Creates and initializes a new instance - /// - /// The - public static ComputationData Create() - { - ComputationData data = default(ComputationData); - data.Unzig = UnzigData.Create(); - return data; - } - } - - /// - /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of - /// - public struct DataPointers - { - /// - /// Pointer to - /// - public Block8x8F* Block; - - /// - /// Pointer to - /// - public Block8x8F* Temp1; - - /// - /// Pointer to - /// - public Block8x8F* Temp2; - - /// - /// Pointer to - /// - public Block8x8F* QuantiazationTable; - - /// - /// Pointer to as int* - /// - public int* Unzig; - - /// - /// Initializes a new instance of the struct. - /// - /// Pointer to - internal DataPointers(ComputationData* dataPtr) - { - this.Block = &dataPtr->Block; - this.Temp1 = &dataPtr->Temp1; - this.Temp2 = &dataPtr->Temp2; - this.QuantiazationTable = &dataPtr->QuantiazationTable; - this.Unzig = dataPtr->Unzig.Data; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs deleted file mode 100644 index 342ce299c..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs +++ /dev/null @@ -1,115 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - - /// - /// Represents an area of a Jpeg subimage (channel) - /// - internal struct JpegPixelArea - { - /// - /// Initializes a new instance of the struct from existing data. - /// - /// The pixel buffer - /// The stride - /// The offset - public JpegPixelArea(Buffer2D pixels, int stride, int offset) - { - this.Stride = stride; - this.Pixels = pixels; - this.Offset = offset; - } - - /// - /// Initializes a new instance of the struct from existing buffer. - /// will be set to of and will be set to 0. - /// - /// The pixel buffer - public JpegPixelArea(Buffer2D pixels) - : this(pixels, pixels.Width, 0) - { - } - - /// - /// Gets the pixels buffer. - /// - public Buffer2D Pixels { get; private set; } - - /// - /// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea)) - /// - public bool IsInitialized => this.Pixels != null; - - /// - /// Gets the stride. - /// - public int Stride { get; } - - /// - /// Gets the offset. - /// - public int Offset { get; } - - /// - /// Gets a of bytes to the pixel area - /// - public MutableSpan Span => new MutableSpan(this.Pixels.Array, this.Offset); - - /// - /// Returns the pixel at (x, y) - /// - /// The x index - /// The y index - /// The pixel value - public byte this[int x, int y] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.Pixels[(y * this.Stride) + x]; - } - } - - /// - /// Gets the subarea that belongs to the Block8x8 defined by block indices - /// - /// The block X index - /// The block Y index - /// The subarea offseted by block indices - public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by) - { - int offset = this.Offset + (8 * ((by * this.Stride) + bx)); - return new JpegPixelArea(this.Pixels, this.Stride, offset); - } - - /// - /// Gets the row offset at the given position - /// - /// The y-coordinate of the image. - /// The - public int GetRowOffset(int y) - { - return this.Offset + (y * this.Stride); - } - - /// - /// Load values to the pixel area from the given . - /// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to - /// values - /// - /// The block holding the color values - /// Temporal block provided by the caller - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp) - { - // Level shift by +128, clip to [0, 255], and write to dst. - block->CopyColorsTo(new MutableSpan(this.Pixels.Array, this.Offset), this.Stride, temp); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs deleted file mode 100644 index c9cc327b8..000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - - using ImageSharp.Memory; - using SixLabors.Primitives; - - /// - /// Represents an image made up of three color components (luminance, blue chroma, red chroma) - /// - internal class YCbCrImage : IDisposable - { - // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P -#pragma warning disable SA1401 // FieldsMustBePrivate - /// - /// Gets the luminance components channel as . - /// - public Buffer2D YChannel; - - /// - /// Gets the blue chroma components channel as . - /// - public Buffer2D CbChannel; - - /// - /// Gets an offseted to the Cr channel - /// - public Buffer2D CrChannel; -#pragma warning restore SA1401 - - /// - /// Initializes a new instance of the class. - /// - /// The width. - /// The height. - /// The ratio. - public YCbCrImage(int width, int height, YCbCrSubsampleRatio ratio) - { - Size cSize = CalculateChrominanceSize(width, height, ratio); - - this.Ratio = ratio; - this.YStride = width; - this.CStride = cSize.Width; - - this.YChannel = Buffer2D.CreateClean(width, height); - this.CbChannel = Buffer2D.CreateClean(cSize.Width, cSize.Height); - this.CrChannel = Buffer2D.CreateClean(cSize.Width, cSize.Height); - } - - /// - /// Provides enumeration of the various available subsample ratios. - /// - public enum YCbCrSubsampleRatio - { - /// - /// YCbCrSubsampleRatio444 - /// - YCbCrSubsampleRatio444, - - /// - /// YCbCrSubsampleRatio422 - /// - YCbCrSubsampleRatio422, - - /// - /// YCbCrSubsampleRatio420 - /// - YCbCrSubsampleRatio420, - - /// - /// YCbCrSubsampleRatio440 - /// - YCbCrSubsampleRatio440, - - /// - /// YCbCrSubsampleRatio411 - /// - YCbCrSubsampleRatio411, - - /// - /// YCbCrSubsampleRatio410 - /// - YCbCrSubsampleRatio410, - } - - /// - /// Gets the Y slice index delta between vertically adjacent pixels. - /// - public int YStride { get; } - - /// - /// Gets the red and blue chroma slice index delta between vertically adjacent pixels - /// that map to separate chroma samples. - /// - public int CStride { get; } - - /// - /// Gets or sets the subsampling ratio. - /// - public YCbCrSubsampleRatio Ratio { get; set; } - - /// - /// Disposes the returning rented arrays to the pools. - /// - public void Dispose() - { - this.YChannel.Dispose(); - this.CbChannel.Dispose(); - this.CrChannel.Dispose(); - } - - /// - /// Returns the offset of the first chroma component at the given row - /// - /// The row number. - /// - /// The . - /// - public int GetRowCOffset(int y) - { - switch (this.Ratio) - { - case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: - return y * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: - return (y / 2) * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: - return (y / 2) * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: - return y * this.CStride; - case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: - return (y / 2) * this.CStride; - default: - return y * this.CStride; - } - } - - /// - /// Returns the offset of the first luminance component at the given row - /// - /// The row number. - /// - /// The . - /// - public int GetRowYOffset(int y) - { - return y * this.YStride; - } - - /// - /// Returns the height and width of the chroma components - /// - /// The width. - /// The height. - /// The subsampling ratio. - /// The of the chrominance channel - internal static Size CalculateChrominanceSize( - int width, - int height, - YCbCrSubsampleRatio ratio) - { - switch (ratio) - { - case YCbCrSubsampleRatio.YCbCrSubsampleRatio422: - return new Size((width + 1) / 2, height); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio420: - return new Size((width + 1) / 2, (height + 1) / 2); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio440: - return new Size(width, (height + 1) / 2); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio411: - return new Size((width + 3) / 4, height); - case YCbCrSubsampleRatio.YCbCrSubsampleRatio410: - return new Size((width + 3) / 4, (height + 1) / 2); - default: - // Default to 4:4:4 subsampling. - return new Size(width, height); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/BlockQuad.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs similarity index 53% rename from src/ImageSharp/Formats/Jpeg/Components/BlockQuad.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs index 63453da21..6b16ea824 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/BlockQuad.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/BlockQuad.cs @@ -1,18 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg.Components + +using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components { /// - /// Poor man's stackalloc: Contains a value-type buffer sized for 4 instances. + /// Poor man's stackalloc: Contains a value-type buffer sized for 4 instances. /// Useful for decoder/encoder operations allocating a block for each Jpeg component. /// internal unsafe struct BlockQuad { /// - /// The value-type buffer sized for 4 instances. + /// The value-type buffer sized for 4 instances. /// - public fixed float Data[4 * Block8x8F.ScalarCount]; + public fixed float Data[4 * Block8x8F.Size]; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bits.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs similarity index 71% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/Bits.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs index 02f585be0..acaa69e3a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bits.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bits.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Holds the unprocessed bits that have been taken from the byte-stream. /// The n least significant bits of a form the unread bits, to be read in MSB to @@ -40,7 +38,7 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EnsureNBits(int n, ref InputProcessor inputProcessor) { - DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); + OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(n, ref inputProcessor); errorCode.EnsureNoError(); } @@ -48,17 +46,17 @@ namespace ImageSharp.Formats.Jpg /// Reads bytes from the byte buffer to ensure that bits.UnreadBits is at /// least n. For best performance (avoiding function calls inside hot loops), /// the caller is the one responsible for first checking that bits.UnreadBits < n. - /// This method does not throw. Returns instead. + /// This method does not throw. Returns instead. /// /// The number of bits to ensure. /// The /// Error code - public DecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) + public OrigDecoderErrorCode EnsureNBitsUnsafe(int n, ref InputProcessor inputProcessor) { while (true) { - DecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); - if (errorCode != DecoderErrorCode.NoError || this.UnreadBits >= n) + OrigDecoderErrorCode errorCode = this.EnsureBitsStepImpl(ref inputProcessor); + if (errorCode != OrigDecoderErrorCode.NoError || this.UnreadBits >= n) { return errorCode; } @@ -69,8 +67,8 @@ namespace ImageSharp.Formats.Jpg /// Unrolled version of for n==8 /// /// The - /// A - public DecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) + /// A + public OrigDecoderErrorCode Ensure8BitsUnsafe(ref InputProcessor inputProcessor) { return this.EnsureBitsStepImpl(ref inputProcessor); } @@ -79,8 +77,8 @@ namespace ImageSharp.Formats.Jpg /// Unrolled version of for n==1 ///
/// The - /// A - public DecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) + /// A + public OrigDecoderErrorCode Ensure1BitUnsafe(ref InputProcessor inputProcessor) { return this.EnsureBitsStepImpl(ref inputProcessor); } @@ -95,7 +93,7 @@ namespace ImageSharp.Formats.Jpg public int ReceiveExtend(int t, ref InputProcessor inputProcessor) { int x; - DecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out x); + OrigDecoderErrorCode errorCode = this.ReceiveExtendUnsafe(t, ref inputProcessor, out x); errorCode.EnsureNoError(); return x; } @@ -106,13 +104,13 @@ namespace ImageSharp.Formats.Jpg /// Byte /// The /// Read bits value - /// The - public DecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) + /// The + public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, ref InputProcessor inputProcessor, out int x) { if (this.UnreadBits < t) { - DecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); - if (errorCode != DecoderErrorCode.NoError) + OrigDecoderErrorCode errorCode = this.EnsureNBitsUnsafe(t, ref inputProcessor); + if (errorCode != OrigDecoderErrorCode.NoError) { x = int.MaxValue; return errorCode; @@ -129,15 +127,15 @@ namespace ImageSharp.Formats.Jpg x += ((-1) << t) + 1; } - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } - private DecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) + private OrigDecoderErrorCode EnsureBitsStepImpl(ref InputProcessor inputProcessor) { int c; - DecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out c); + OrigDecoderErrorCode errorCode = inputProcessor.Bytes.ReadByteStuffedByteUnsafe(inputProcessor.InputStream, out c); - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { return errorCode; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bytes.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs similarity index 76% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/Bytes.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs index 0e57e98d8..b8c64fbe4 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/Bytes.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/Bytes.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - using System.IO; - using System.Runtime.CompilerServices; +using System; +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Bytes is a byte buffer, similar to a stream, except that it /// has to be able to unread more than 1 byte, due to byte stuffing. @@ -86,8 +85,8 @@ namespace ImageSharp.Formats.Jpg /// /// Input stream /// The result byte as - /// The - public DecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) + /// The + public OrigDecoderErrorCode ReadByteStuffedByteUnsafe(Stream inputStream, out int x) { // Take the fast path if bytes.buf contains at least two bytes. if (this.I + 2 <= this.J) @@ -95,50 +94,50 @@ namespace ImageSharp.Formats.Jpg x = this.BufferAsInt[this.I]; this.I++; this.UnreadableBytes = 1; - if (x != JpegConstants.Markers.XFFInt) + if (x != OrigJpegConstants.Markers.XFFInt) { - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } if (this.BufferAsInt[this.I] != 0x00) { - return DecoderErrorCode.MissingFF00; + return OrigDecoderErrorCode.MissingFF00; } this.I++; this.UnreadableBytes = 2; - x = JpegConstants.Markers.XFF; - return DecoderErrorCode.NoError; + x = OrigJpegConstants.Markers.XFF; + return OrigDecoderErrorCode.NoError; } this.UnreadableBytes = 0; - DecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); + OrigDecoderErrorCode errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); this.UnreadableBytes = 1; - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { return errorCode; } - if (x != JpegConstants.Markers.XFF) + if (x != OrigJpegConstants.Markers.XFF) { - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } errorCode = this.ReadByteAsIntUnsafe(inputStream, out x); this.UnreadableBytes = 2; - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { return errorCode; } if (x != 0x00) { - return DecoderErrorCode.MissingFF00; + return OrigDecoderErrorCode.MissingFF00; } - x = JpegConstants.Markers.XFF; - return DecoderErrorCode.NoError; + x = OrigJpegConstants.Markers.XFF; + return OrigDecoderErrorCode.NoError; } /// @@ -150,25 +149,25 @@ namespace ImageSharp.Formats.Jpg public byte ReadByte(Stream inputStream) { byte result; - DecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out result); + OrigDecoderErrorCode errorCode = this.ReadByteUnsafe(inputStream, out result); errorCode.EnsureNoError(); return result; } /// /// Extracts the next byte, whether buffered or not buffered into the result out parameter. It does not care about byte stuffing. - /// This method does not throw on format error, it returns a instead. + /// This method does not throw on format error, it returns a instead. /// /// Input stream /// The result as out parameter - /// The - public DecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) + /// The + public OrigDecoderErrorCode ReadByteUnsafe(Stream inputStream, out byte result) { - DecoderErrorCode errorCode = DecoderErrorCode.NoError; + OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError; while (this.I == this.J) { errorCode = this.FillUnsafe(inputStream); - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { result = 0; return errorCode; @@ -186,15 +185,15 @@ namespace ImageSharp.Formats.Jpg /// /// The input stream /// The result - /// A + /// A [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) + public OrigDecoderErrorCode ReadByteAsIntUnsafe(Stream inputStream, out int result) { - DecoderErrorCode errorCode = DecoderErrorCode.NoError; + OrigDecoderErrorCode errorCode = OrigDecoderErrorCode.NoError; while (this.I == this.J) { errorCode = this.FillUnsafe(inputStream); - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { result = 0; return errorCode; @@ -216,18 +215,18 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Fill(Stream inputStream) { - DecoderErrorCode errorCode = this.FillUnsafe(inputStream); + OrigDecoderErrorCode errorCode = this.FillUnsafe(inputStream); errorCode.EnsureNoError(); } /// /// Fills up the bytes buffer from the underlying stream. /// It should only be called when there are no unread bytes in bytes. - /// This method does not throw , returns a instead! + /// This method does not throw , returns a instead! /// /// Input stream - /// The - public DecoderErrorCode FillUnsafe(Stream inputStream) + /// The + public OrigDecoderErrorCode FillUnsafe(Stream inputStream) { if (this.I != this.J) { @@ -249,7 +248,7 @@ namespace ImageSharp.Formats.Jpg int n = inputStream.Read(this.Buffer, this.J, this.Buffer.Length - this.J); if (n == 0) { - return DecoderErrorCode.UnexpectedEndOfStream; + return OrigDecoderErrorCode.UnexpectedEndOfStream; } this.J += n; @@ -259,7 +258,7 @@ namespace ImageSharp.Formats.Jpg this.BufferAsInt[i] = this.Buffer[i]; } - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs similarity index 65% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderThrowHelper.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs index 9ce5ea414..d5a9340d7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/DecoderThrowHelper.cs @@ -1,32 +1,30 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Encapsulates exception thrower methods for the Jpeg Encoder /// internal static class DecoderThrowHelper { /// - /// Throws an exception that belongs to the given + /// Throws an exception that belongs to the given /// - /// The + /// The [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowExceptionForErrorCode(this DecoderErrorCode errorCode) + public static void ThrowExceptionForErrorCode(this OrigDecoderErrorCode errorCode) { switch (errorCode) { - case DecoderErrorCode.NoError: + case OrigDecoderErrorCode.NoError: throw new ArgumentException("ThrowExceptionForErrorCode() called with NoError!", nameof(errorCode)); - case DecoderErrorCode.MissingFF00: + case OrigDecoderErrorCode.MissingFF00: throw new MissingFF00Exception(); - case DecoderErrorCode.UnexpectedEndOfStream: + case OrigDecoderErrorCode.UnexpectedEndOfStream: throw new EOFException(); default: throw new ArgumentOutOfRangeException(nameof(errorCode), errorCode, null); @@ -34,26 +32,26 @@ namespace ImageSharp.Formats.Jpg } /// - /// Throws an exception if the given defines an error. + /// Throws an exception if the given defines an error. /// - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoError(this DecoderErrorCode errorCode) + public static void EnsureNoError(this OrigDecoderErrorCode errorCode) { - if (errorCode != DecoderErrorCode.NoError) + if (errorCode != OrigDecoderErrorCode.NoError) { ThrowExceptionForErrorCode(errorCode); } } /// - /// Throws an exception if the given is . + /// Throws an exception if the given is . /// - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureNoEOF(this DecoderErrorCode errorCode) + public static void EnsureNoEOF(this OrigDecoderErrorCode errorCode) { - if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + if (errorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) { errorCode.ThrowExceptionForErrorCode(); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/EOFException.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs similarity index 75% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/EOFException.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs index 5ed25ef04..60d9b1e1a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/EOFException.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/EOFException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// The EOF (End of File exception). /// Thrown when the decoder encounters an EOF marker without a proceeding EOI (End Of Image) marker diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/InputProcessor.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs similarity index 66% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/InputProcessor.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs index 60042d36f..a7a5fcd98 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/InputProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/InputProcessor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; +using System; +using System.IO; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// - /// Encapsulates stream reading and processing data and operations for . + /// Encapsulates stream reading and processing data and operations for . /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s /// internal struct InputProcessor : IDisposable @@ -29,14 +27,14 @@ namespace ImageSharp.Formats.Jpg /// Initializes a new instance of the struct. /// /// The input - /// Temporal buffer, same as + /// Temporal buffer, same as public InputProcessor(Stream inputStream, byte[] temp) { this.Bits = default(Bits); this.Bytes = Bytes.Create(); this.InputStream = inputStream; this.Temp = temp; - this.UnexpectedEndOfStreamReached = false; + this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// @@ -45,53 +43,53 @@ namespace ImageSharp.Formats.Jpg public Stream InputStream { get; } /// - /// Gets the temporal buffer, same instance as + /// Gets the temporal buffer, same instance as /// public byte[] Temp { get; } /// - /// Gets or sets a value indicating whether an unexpected EOF reached in . + /// Gets a value indicating whether an unexpected EOF reached in . /// - public bool UnexpectedEndOfStreamReached { get; set; } + public bool ReachedEOF => this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream; + + public bool HasError => this.LastErrorCode != OrigDecoderErrorCode.NoError; + + public OrigDecoderErrorCode LastErrorCode { get; private set; } + + public void ResetErrorState() => this.LastErrorCode = OrigDecoderErrorCode.NoError; /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. + /// If errorCode indicates unexpected EOF, sets to true and returns false. /// Calls and returns true otherwise. /// - /// The - /// indicating whether everything is OK - public bool CheckEOFEnsureNoError(DecoderErrorCode errorCode) + /// A indicating whether EOF reached + public bool CheckEOFEnsureNoError() { - if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) { - this.UnexpectedEndOfStreamReached = true; return false; } - errorCode.EnsureNoError(); + this.LastErrorCode.EnsureNoError(); return true; } /// - /// If errorCode indicates unexpected EOF, sets to true and returns false. + /// If errorCode indicates unexpected EOF, sets to true and returns false. /// Returns true otherwise. /// - /// The - /// indicating whether everything is OK - public bool CheckEOF(DecoderErrorCode errorCode) + /// A indicating whether EOF reached + public bool CheckEOF() { - if (errorCode == DecoderErrorCode.UnexpectedEndOfStream) + if (this.LastErrorCode == OrigDecoderErrorCode.UnexpectedEndOfStream) { - this.UnexpectedEndOfStreamReached = true; return false; } return true; } - /// - /// Dispose - /// + /// public void Dispose() { this.Bytes.Dispose(); @@ -112,34 +110,34 @@ namespace ImageSharp.Formats.Jpg /// TODO: This method (and also the usages) could be optimized by batching! /// /// The decoded bit as a - /// The - public DecoderErrorCode DecodeBitUnsafe(out bool result) + /// The + public OrigDecoderErrorCode DecodeBitUnsafe(out bool result) { if (this.Bits.UnreadBits == 0) { - DecoderErrorCode errorCode = this.Bits.Ensure1BitUnsafe(ref this); - if (errorCode != DecoderErrorCode.NoError) + this.LastErrorCode = this.Bits.Ensure1BitUnsafe(ref this); + if (this.LastErrorCode != OrigDecoderErrorCode.NoError) { result = false; - return errorCode; + return this.LastErrorCode; } } result = (this.Bits.Accumulator & this.Bits.Mask) != 0; this.Bits.UnreadBits--; this.Bits.Mask >>= 1; - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// /// Reads exactly length bytes into data. It does not care about byte stuffing. - /// Does not throw on errors, returns instead! + /// Does not throw on errors, returns instead! /// /// The data to write to. /// The offset in the source buffer /// The number of bytes to read - /// The - public DecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) + /// The + public OrigDecoderErrorCode ReadFullUnsafe(byte[] data, int offset, int length) { // Unread the overshot bytes, if any. if (this.Bytes.UnreadableBytes != 0) @@ -152,8 +150,8 @@ namespace ImageSharp.Formats.Jpg this.Bytes.UnreadableBytes = 0; } - DecoderErrorCode errorCode = DecoderErrorCode.NoError; - while (length > 0) + this.LastErrorCode = OrigDecoderErrorCode.NoError; + while (length > 0 && this.LastErrorCode == OrigDecoderErrorCode.NoError) { if (this.Bytes.J - this.Bytes.I >= length) { @@ -168,11 +166,11 @@ namespace ImageSharp.Formats.Jpg length -= this.Bytes.J - this.Bytes.I; this.Bytes.I += this.Bytes.J - this.Bytes.I; - errorCode = this.Bytes.FillUnsafe(this.InputStream); + this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); } } - return errorCode; + return this.LastErrorCode; } /// @@ -180,19 +178,24 @@ namespace ImageSharp.Formats.Jpg /// /// The number of bits to decode. /// The result - /// The - public DecoderErrorCode DecodeBitsUnsafe(int count, out int result) + /// The + public OrigDecoderErrorCode DecodeBitsUnsafe(int count, out int result) { if (this.Bits.UnreadBits < count) { - this.Bits.EnsureNBits(count, ref this); + this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(count, ref this); + if (this.LastErrorCode != OrigDecoderErrorCode.NoError) + { + result = 0; + return this.LastErrorCode; + } } result = this.Bits.Accumulator >> (this.Bits.UnreadBits - count); result = result & ((1 << count) - 1); this.Bits.UnreadBits -= count; this.Bits.Mask >>= count; - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// @@ -200,8 +203,8 @@ namespace ImageSharp.Formats.Jpg /// /// The huffman value /// The decoded - /// The - public DecoderErrorCode DecodeHuffmanUnsafe(ref HuffmanTree huffmanTree, out int result) + /// The + public OrigDecoderErrorCode DecodeHuffmanUnsafe(ref OrigHuffmanTree huffmanTree, out int result) { result = 0; @@ -212,11 +215,11 @@ namespace ImageSharp.Formats.Jpg if (this.Bits.UnreadBits < 8) { - DecoderErrorCode errorCode = this.Bits.Ensure8BitsUnsafe(ref this); + this.LastErrorCode = this.Bits.Ensure8BitsUnsafe(ref this); - if (errorCode == DecoderErrorCode.NoError) + if (this.LastErrorCode == OrigDecoderErrorCode.NoError) { - int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - HuffmanTree.LutSizeLog2)) & 0xFF; + int lutIndex = (this.Bits.Accumulator >> (this.Bits.UnreadBits - OrigHuffmanTree.LutSizeLog2)) & 0xFF; int v = huffmanTree.Lut[lutIndex]; if (v != 0) @@ -225,22 +228,27 @@ namespace ImageSharp.Formats.Jpg this.Bits.UnreadBits -= n; this.Bits.Mask >>= n; result = v >> 8; - return errorCode; + return this.LastErrorCode; } } else { this.UnreadByteStuffedByte(); - return errorCode; + return this.LastErrorCode; } } int code = 0; - for (int i = 0; i < HuffmanTree.MaxCodeLength; i++) + for (int i = 0; i < OrigHuffmanTree.MaxCodeLength; i++) { if (this.Bits.UnreadBits == 0) { - this.Bits.EnsureNBits(1, ref this); + this.LastErrorCode = this.Bits.EnsureNBitsUnsafe(1, ref this); + + if (this.HasError) + { + return this.LastErrorCode; + } } if ((this.Bits.Accumulator & this.Bits.Mask) != 0) @@ -254,7 +262,7 @@ namespace ImageSharp.Formats.Jpg if (code <= huffmanTree.MaxCodes[i]) { result = huffmanTree.GetValue(code, i); - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } code <<= 1; @@ -264,7 +272,7 @@ namespace ImageSharp.Formats.Jpg DecoderThrowHelper.ThrowImageFormatException.BadHuffmanCode(); // DUMMY RETURN! C# doesn't know we have thrown an exception! - return DecoderErrorCode.NoError; + return OrigDecoderErrorCode.NoError; } /// @@ -274,17 +282,17 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Skip(int count) { - DecoderErrorCode errorCode = this.SkipUnsafe(count); - errorCode.EnsureNoError(); + this.LastErrorCode = this.SkipUnsafe(count); + this.LastErrorCode.EnsureNoError(); } /// /// Skips the next n bytes. - /// Does not throw, returns instead! + /// Does not throw, returns instead! /// /// The number of bytes to ignore. - /// The - public DecoderErrorCode SkipUnsafe(int count) + /// The + public OrigDecoderErrorCode SkipUnsafe(int count) { // Unread the overshot bytes, if any. if (this.Bytes.UnreadableBytes != 0) @@ -312,14 +320,14 @@ namespace ImageSharp.Formats.Jpg break; } - DecoderErrorCode errorCode = this.Bytes.FillUnsafe(this.InputStream); - if (errorCode != DecoderErrorCode.NoError) + this.LastErrorCode = this.Bytes.FillUnsafe(this.InputStream); + if (this.LastErrorCode != OrigDecoderErrorCode.NoError) { - return errorCode; + return this.LastErrorCode; } } - return DecoderErrorCode.NoError; + return this.LastErrorCode = OrigDecoderErrorCode.NoError; } /// @@ -331,8 +339,8 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadFull(byte[] data, int offset, int length) { - DecoderErrorCode errorCode = this.ReadFullUnsafe(data, offset, length); - errorCode.EnsureNoError(); + this.LastErrorCode = this.ReadFullUnsafe(data, offset, length); + this.LastErrorCode.EnsureNoError(); } /// @@ -359,10 +367,11 @@ namespace ImageSharp.Formats.Jpg /// /// Byte /// Read bits value - /// The - public DecoderErrorCode ReceiveExtendUnsafe(int t, out int x) + /// The + public OrigDecoderErrorCode ReceiveExtendUnsafe(int t, out int x) { - return this.Bits.ReceiveExtendUnsafe(t, ref this, out x); + this.LastErrorCode = this.Bits.ReceiveExtendUnsafe(t, ref this, out x); + return this.LastErrorCode; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.md b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md similarity index 100% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.md rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/JpegScanDecoder.md diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/MissingFF00Exception.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs similarity index 54% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/MissingFF00Exception.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs index f8c157237..005034b9d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/MissingFF00Exception.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/MissingFF00Exception.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// The missing ff00 exception. /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs new file mode 100644 index 000000000..c87752b37 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponent.cs @@ -0,0 +1,244 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ + /// + /// + /// Represents a single color component + /// + internal class OrigComponent : IDisposable, IJpegComponent + { + public OrigComponent(byte identifier, int index) + { + this.Identifier = identifier; + this.Index = index; + } + + /// + /// Gets the identifier + /// + public byte Identifier { get; } + + /// + public int Index { get; } + + public Size SizeInBlocks { get; private set; } + + public Size SamplingFactors { get; private set; } + + public Size SubSamplingDivisors { get; private set; } + + public int HorizontalSamplingFactor => this.SamplingFactors.Width; + + public int VerticalSamplingFactor => this.SamplingFactors.Height; + + /// + public int QuantizationTableIndex { get; private set; } + + /// + /// + /// Gets the storing the "raw" frequency-domain decoded blocks. + /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. + /// This is done by . + /// When us true, we are touching these blocks multiple times - each time we process a Scan. + /// + public Buffer2D SpectralBlocks { get; private set; } + + /// + /// Initializes + /// + /// The instance + public void InitializeDerivedData(OrigJpegDecoderCore decoder) + { + // For 4-component images (either CMYK or YCbCrK), we only support two + // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. + // Theoretically, 4-component JPEG images could mix and match hv values + // but in practice, those two combinations are the only ones in use, + // and it simplifies the applyBlack code below if we can assume that: + // - for CMYK, the C and K channels have full samples, and if the M + // and Y channels subsample, they subsample both horizontally and + // vertically. + // - for YCbCrK, the Y and K channels have full samples. + this.SizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(this.SamplingFactors); + + if (this.Index == 0 || this.Index == 3) + { + this.SubSamplingDivisors = new Size(1, 1); + } + else + { + OrigComponent c0 = decoder.Components[0]; + this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); + } + + this.SpectralBlocks = Buffer2D.CreateClean(this.SizeInBlocks); + } + + /// + /// Initializes all component data except . + /// + /// The instance + public void InitializeCoreData(OrigJpegDecoderCore decoder) + { + // Section B.2.2 states that "the value of C_i shall be different from + // the values of C_1 through C_(i-1)". + int i = this.Index; + + for (int j = 0; j < this.Index; j++) + { + if (this.Identifier == decoder.Components[j].Identifier) + { + throw new ImageFormatException("Repeated component identifier"); + } + } + + this.QuantizationTableIndex = decoder.Temp[8 + (3 * i)]; + if (this.QuantizationTableIndex > OrigJpegDecoderCore.MaxTq) + { + throw new ImageFormatException("Bad Tq value"); + } + + byte hv = decoder.Temp[7 + (3 * i)]; + int h = hv >> 4; + int v = hv & 0x0f; + if (h < 1 || h > 4 || v < 1 || v > 4) + { + throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio"); + } + + if (h == 3 || v == 3) + { + throw new ImageFormatException("Lnsupported subsampling ratio"); + } + + switch (decoder.ComponentCount) + { + case 1: + + // If a JPEG image has only one component, section A.2 says "this data + // is non-interleaved by definition" and section A.2.2 says "[in this + // case...] the order of data units within a scan shall be left-to-right + // and top-to-bottom... regardless of the values of H_1 and V_1". Section + // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be + // one data unit". Similarly, section A.1.1 explains that it is the ratio + // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale + // images, H_1 is the maximum H_j for all components j, so that ratio is + // always 1. The component's (h, v) is effectively always (1, 1): even if + // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 + // MCUs, not two 16x8 MCUs. + h = 1; + v = 1; + break; + + case 3: + + // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, + // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the + // (h, v) values for the Y component are either (1, 1), (1, 2), + // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values + // must be a multiple of the Cb and Cr component's values. We also + // assume that the two chroma components have the same subsampling + // ratio. + switch (i) + { + case 0: + { + // Y. + // We have already verified, above, that h and v are both + // either 1, 2 or 4, so invalid (h, v) combinations are those + // with v == 4. + if (v == 4) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + case 1: + { + // Cb. + Size s0 = decoder.Components[0].SamplingFactors; + + if (s0.Width % h != 0 || s0.Height % v != 0) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + case 2: + { + // Cr. + Size s1 = decoder.Components[1].SamplingFactors; + + if (s1.Width != h || s1.Height != v) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + } + + break; + + case 4: + + // For 4-component images (either CMYK or YCbCrK), we only support two + // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. + // Theoretically, 4-component JPEG images could mix and match hv values + // but in practice, those two combinations are the only ones in use, + // and it simplifies the applyBlack code below if we can assume that: + // - for CMYK, the C and K channels have full samples, and if the M + // and Y channels subsample, they subsample both horizontally and + // vertically. + // - for YCbCrK, the Y and K channels have full samples. + switch (i) + { + case 0: + if (hv != 0x11 && hv != 0x22) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + case 1: + case 2: + if (hv != 0x11) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + case 3: + Size s0 = decoder.Components[0].SamplingFactors; + + if (s0.Width != h || s0.Height != v) + { + throw new ImageFormatException("Unsupported subsampling ratio"); + } + + break; + } + + break; + } + + this.SamplingFactors = new Size(h, v); + } + + public void Dispose() + { + this.SpectralBlocks.Dispose(); + } + } +} diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentScan.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs similarity index 69% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentScan.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs index 89fc115ea..0d9804404 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentScan.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigComponentScan.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Represents a component scan /// [StructLayout(LayoutKind.Sequential)] - internal struct ComponentScan + internal struct OrigComponentScan { /// /// Gets or sets the component index. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderErrorCode.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs similarity index 70% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderErrorCode.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs index 8b82184fa..02a8ea55e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecoderErrorCode.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigDecoderErrorCode.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// /// Represents "recoverable" decoder errors. /// - internal enum DecoderErrorCode + internal enum OrigDecoderErrorCode { /// /// NoError diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTree.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs index 390e5dd15..4c97d5741 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigHuffmanTree.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; +using System; +using System.Buffers; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Represents a Huffman tree /// - internal struct HuffmanTree : IDisposable + internal struct OrigHuffmanTree : IDisposable { /// /// The index of the AC table row @@ -99,12 +98,12 @@ namespace ImageSharp.Formats.Jpg private static readonly ArrayPool CodesPool16 = ArrayPool.Create(MaxCodeLength, 50); /// - /// Creates and initializes an array of instances of size + /// Creates and initializes an array of instances of size /// - /// An array of instances representing the Huffman tables - public static HuffmanTree[] CreateHuffmanTrees() + /// An array of instances representing the Huffman tables + public static OrigHuffmanTree[] CreateHuffmanTrees() { - HuffmanTree[] result = new HuffmanTree[NumberOfTrees]; + OrigHuffmanTree[] result = new OrigHuffmanTree[NumberOfTrees]; for (int i = 0; i < MaxTc + 1; i++) { for (int j = 0; j < MaxTh + 1; j++) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs similarity index 66% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs index 7b910cdd2..6252d8209 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs @@ -1,16 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Conains the definition of /// - internal unsafe partial struct JpegScanDecoder + internal unsafe partial struct OrigJpegScanDecoder { /// /// Holds the "large" data blocks needed for computations. @@ -21,7 +21,7 @@ namespace ImageSharp.Formats.Jpg /// /// The main input/working block /// - public Block8x8F Block; + public Block8x8 Block; /// /// The jpeg unzig data @@ -29,14 +29,14 @@ namespace ImageSharp.Formats.Jpg public UnzigData Unzig; /// - /// The buffer storing the -s for each component + /// The buffer storing the -s for each component /// - public fixed byte ScanData[3 * JpegDecoderCore.MaxComponents]; + public fixed byte ScanData[3 * OrigJpegDecoderCore.MaxComponents]; /// /// The DC values for each component /// - public fixed int Dc[JpegDecoderCore.MaxComponents]; + public fixed int Dc[OrigJpegDecoderCore.MaxComponents]; /// /// Creates and initializes a new instance diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs similarity index 78% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs index 52e25f3a8..0098b4a4e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.DataPointers.cs @@ -1,14 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg +using SixLabors.ImageSharp.Formats.Jpeg.Common; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder { /// /// Conains the definition of /// - internal unsafe partial struct JpegScanDecoder + internal unsafe partial struct OrigJpegScanDecoder { /// /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of @@ -18,7 +18,7 @@ namespace ImageSharp.Formats.Jpg /// /// Pointer to /// - public Block8x8F* Block; + public Block8x8* Block; /// /// Pointer to as int* @@ -28,7 +28,7 @@ namespace ImageSharp.Formats.Jpg /// /// Pointer to as Scan* /// - public ComponentScan* ComponentScan; + public OrigComponentScan* ComponentScan; /// /// Pointer to @@ -43,7 +43,7 @@ namespace ImageSharp.Formats.Jpg { this.Block = &basePtr->Block; this.Unzig = basePtr->Unzig.Data; - this.ComponentScan = (ComponentScan*)basePtr->ScanData; + this.ComponentScan = (OrigComponentScan*)basePtr->ScanData; this.Dc = basePtr->Dc; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs similarity index 74% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs index 7d2e6d441..b3a7bc4af 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -// ReSharper disable InconsistentNaming -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.Memory; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder +{ /// /// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md! /// @@ -30,7 +29,7 @@ namespace ImageSharp.Formats.Jpg /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. /// [StructLayout(LayoutKind.Sequential)] - internal unsafe partial struct JpegScanDecoder + internal unsafe partial struct OrigJpegScanDecoder { // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. #pragma warning disable SA1202 // ElementsMustBeOrderedByAccess @@ -96,12 +95,12 @@ namespace ImageSharp.Formats.Jpg private int eobRun; /// - /// Initializes a default-constructed instance for reading data from -s stream. + /// Initializes a default-constructed instance for reading data from -s stream. /// - /// Pointer to on the stack - /// The instance + /// Pointer to on the stack + /// The instance /// The remaining bytes in the segment block. - public static void InitStreamReading(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) + public static void InitStreamReading(OrigJpegScanDecoder* p, OrigJpegDecoderCore decoder, int remaining) { p->data = ComputationData.Create(); p->pointers = new DataPointers(&p->data); @@ -109,8 +108,8 @@ namespace ImageSharp.Formats.Jpg } /// - /// Read Huffman data from Jpeg scans in , - /// and decode it as into . + /// Read Huffman data from Jpeg scans in , + /// and decode it as into . /// /// The blocks are traversed one MCU at a time. For 4:2:0 chroma /// subsampling, there are four Y 8x8 blocks in every 16x16 MCU. @@ -135,12 +134,14 @@ namespace ImageSharp.Formats.Jpg /// 0 1 2 /// 3 4 5 /// - /// The instance - public void DecodeBlocks(JpegDecoderCore decoder) + /// The instance + public void DecodeBlocks(OrigJpegDecoderCore decoder) { + decoder.InputProcessor.ResetErrorState(); + int blockCount = 0; int mcu = 0; - byte expectedRst = JpegConstants.Markers.RST0; + byte expectedRst = OrigJpegConstants.Markers.RST0; for (int my = 0; my < decoder.MCUCountY; my++) { @@ -149,8 +150,10 @@ namespace ImageSharp.Formats.Jpg for (int scanIndex = 0; scanIndex < this.componentScanCount; scanIndex++) { this.ComponentIndex = this.pointers.ComponentScan[scanIndex].ComponentIndex; - this.hi = decoder.ComponentArray[this.ComponentIndex].HorizontalFactor; - int vi = decoder.ComponentArray[this.ComponentIndex].VerticalFactor; + OrigComponent component = decoder.Components[this.ComponentIndex]; + + this.hi = component.HorizontalSamplingFactor; + int vi = component.VerticalSamplingFactor; for (int j = 0; j < this.hi * vi; j++) { @@ -171,18 +174,19 @@ namespace ImageSharp.Formats.Jpg } } - // Take an existing block (required when progressive): - int blockIndex = this.GetBlockIndex(decoder); - this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block; + // Find the block at (bx,by) in the component's buffer: + ref Block8x8 blockRefOnHeap = ref component.GetBlockReference(this.bx, this.by); + + // Copy block to stack + this.data.Block = blockRefOnHeap; - if (!decoder.InputProcessor.UnexpectedEndOfStreamReached) + if (!decoder.InputProcessor.ReachedEOF) { this.DecodeBlock(decoder, scanIndex); } - // Store the decoded block - Buffer blocks = decoder.DecodedBlocks[this.ComponentIndex]; - blocks[blockIndex].SaveBlock(this.bx, this.by, ref this.data.Block); + // Store the result block: + blockRefOnHeap = this.data.Block; } // for j @@ -195,10 +199,10 @@ namespace ImageSharp.Formats.Jpg { // A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input, // but this one assumes well-formed input, and hence the restart marker follows immediately. - if (!decoder.InputProcessor.UnexpectedEndOfStreamReached) + if (!decoder.InputProcessor.ReachedEOF) { - DecoderErrorCode errorCode = decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); - if (decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + decoder.InputProcessor.ReadFullUnsafe(decoder.Temp, 0, 2); + if (decoder.InputProcessor.CheckEOFEnsureNoError()) { if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) { @@ -206,9 +210,9 @@ namespace ImageSharp.Formats.Jpg } expectedRst++; - if (expectedRst == JpegConstants.Markers.RST7 + 1) + if (expectedRst == OrigJpegConstants.Markers.RST7 + 1) { - expectedRst = JpegConstants.Markers.RST0; + expectedRst = OrigJpegConstants.Markers.RST0; } } } @@ -230,15 +234,15 @@ namespace ImageSharp.Formats.Jpg private void ResetDc() { - Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); + Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * OrigJpegDecoderCore.MaxComponents); } /// /// The implementation part of as an instance method. /// - /// The + /// The /// The remaining bytes - private void InitStreamReadingImpl(JpegDecoderCore decoder, int remaining) + private void InitStreamReadingImpl(OrigJpegDecoderCore decoder, int remaining) { if (decoder.ComponentCount == 0) { @@ -273,7 +277,7 @@ namespace ImageSharp.Formats.Jpg throw new ImageFormatException("Total sampling factors too large."); } - this.zigEnd = Block8x8F.ScalarCount - 1; + this.zigEnd = Block8x8F.Size - 1; if (decoder.IsProgressive) { @@ -283,7 +287,7 @@ namespace ImageSharp.Formats.Jpg this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f; if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd - || this.zigEnd >= Block8x8F.ScalarCount) + || this.zigEnd >= Block8x8F.Size) { throw new ImageFormatException("Bad spectral selection bounds"); } @@ -305,10 +309,10 @@ namespace ImageSharp.Formats.Jpg /// /// The decoder /// The index of the scan - private void DecodeBlock(JpegDecoderCore decoder, int scanIndex) + private void DecodeBlock(OrigJpegDecoderCore decoder, int scanIndex) { - Block8x8F* b = this.pointers.Block; - int huffmannIdx = (HuffmanTree.AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; + Block8x8* b = this.pointers.Block; + int huffmannIdx = (OrigHuffmanTree.AcTableIndex * OrigHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); @@ -316,18 +320,18 @@ namespace ImageSharp.Formats.Jpg else { int zig = this.zigStart; - DecoderErrorCode errorCode; + if (zig == 0) { zig++; // Decode the DC coefficient, as specified in section F.2.2.1. int value; - int huffmanIndex = (HuffmanTree.DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; - errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe( + int huffmanIndex = (OrigHuffmanTree.DcTableIndex * OrigHuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; + decoder.InputProcessor.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], out value); - if (!decoder.InputProcessor.CheckEOF(errorCode)) + if (!decoder.InputProcessor.CheckEOF()) { return; } @@ -338,8 +342,8 @@ namespace ImageSharp.Formats.Jpg } int deltaDC; - errorCode = decoder.InputProcessor.ReceiveExtendUnsafe(value, out deltaDC); - if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + decoder.InputProcessor.ReceiveExtendUnsafe(value, out deltaDC); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) { return; } @@ -347,7 +351,8 @@ namespace ImageSharp.Formats.Jpg this.pointers.Dc[this.ComponentIndex] += deltaDC; // b[0] = dc[compIndex] << al; - Block8x8F.SetScalarAt(b, 0, this.pointers.Dc[this.ComponentIndex] << this.al); + value = this.pointers.Dc[this.ComponentIndex] << this.al; + Block8x8.SetScalarAt(b, 0, (short)value); } if (zig <= this.zigEnd && this.eobRun > 0) @@ -360,8 +365,8 @@ namespace ImageSharp.Formats.Jpg for (; zig <= this.zigEnd; zig++) { int value; - errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); - if (!decoder.InputProcessor.CheckEOF(errorCode)) + decoder.InputProcessor.DecodeHuffmanUnsafe(ref decoder.HuffmanTrees[huffmannIdx], out value); + if (decoder.InputProcessor.HasError) { return; } @@ -377,14 +382,15 @@ namespace ImageSharp.Formats.Jpg } int ac; - errorCode = decoder.InputProcessor.ReceiveExtendUnsafe(val1, out ac); - if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + decoder.InputProcessor.ReceiveExtendUnsafe(val1, out ac); + if (decoder.InputProcessor.HasError) { return; } // b[Unzig[zig]] = ac << al; - Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], ac << this.al); + value = ac << this.al; + Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)value); } else { @@ -393,8 +399,8 @@ namespace ImageSharp.Formats.Jpg this.eobRun = (ushort)(1 << val0); if (val0 != 0) { - errorCode = this.DecodeEobRun(val0, ref decoder.InputProcessor); - if (!decoder.InputProcessor.CheckEOFEnsureNoError(errorCode)) + this.DecodeEobRun(val0, ref decoder.InputProcessor); + if (!decoder.InputProcessor.CheckEOFEnsureNoError()) { return; } @@ -411,30 +417,29 @@ namespace ImageSharp.Formats.Jpg } } - private DecoderErrorCode DecodeEobRun(int count, ref InputProcessor decoder) + private void DecodeEobRun(int count, ref InputProcessor processor) { int bitsResult; - DecoderErrorCode errorCode = decoder.DecodeBitsUnsafe(count, out bitsResult); - if (errorCode != DecoderErrorCode.NoError) + processor.DecodeBitsUnsafe(count, out bitsResult); + if (processor.LastErrorCode != OrigDecoderErrorCode.NoError) { - return errorCode; + return; } this.eobRun |= bitsResult; - return DecoderErrorCode.NoError; } /// - /// Gets the block index used to retieve blocks from in + /// Gets the block index used to retieve blocks from in /// - /// The instance + /// The instance /// The index - private int GetBlockIndex(JpegDecoderCore decoder) + private int GetBlockIndex(OrigJpegDecoderCore decoder) { return ((this.by * decoder.MCUCountX) * this.hi) + this.bx; } - private void InitComponentScan(JpegDecoderCore decoder, int i, ref ComponentScan currentComponentScan, ref int totalHv) + private void InitComponentScan(OrigJpegDecoderCore decoder, int i, ref OrigComponentScan currentComponentScan, ref int totalHv) { // Component selector. int cs = decoder.Temp[1 + (2 * i)]; @@ -442,7 +447,7 @@ namespace ImageSharp.Formats.Jpg for (int j = 0; j < decoder.ComponentCount; j++) { // Component compv = ; - if (cs == decoder.ComponentArray[j].Identifier) + if (cs == decoder.Components[j].Identifier) { compIndex = j; } @@ -455,15 +460,15 @@ namespace ImageSharp.Formats.Jpg currentComponentScan.ComponentIndex = (byte)compIndex; - this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); + this.ProcessComponentImpl(decoder, i, ref currentComponentScan, ref totalHv, decoder.Components[compIndex]); } private void ProcessComponentImpl( - JpegDecoderCore decoder, + OrigJpegDecoderCore decoder, int i, - ref ComponentScan currentComponentScan, + ref OrigComponentScan currentComponentScan, ref int totalHv, - ref Component currentComponent) + OrigComponent currentComponent) { // Section B.2.3 states that "the value of Cs_j shall be different from // the values of Cs_1 through Cs_(j-1)". Since we have previously @@ -478,16 +483,16 @@ namespace ImageSharp.Formats.Jpg } } - totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; + totalHv += currentComponent.HorizontalSamplingFactor * currentComponent.VerticalSamplingFactor; currentComponentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); - if (currentComponentScan.DcTableSelector > HuffmanTree.MaxTh) + if (currentComponentScan.DcTableSelector > OrigHuffmanTree.MaxTh) { throw new ImageFormatException("Bad DC table selector value"); } currentComponentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); - if (currentComponentScan.AcTableSelector > HuffmanTree.MaxTh) + if (currentComponentScan.AcTableSelector > OrigHuffmanTree.MaxTh) { throw new ImageFormatException("Bad AC table selector value"); } @@ -499,9 +504,9 @@ namespace ImageSharp.Formats.Jpg /// The instance /// The Huffman tree /// The low transform offset - private void Refine(ref InputProcessor bp, ref HuffmanTree h, int delta) + private void Refine(ref InputProcessor bp, ref OrigHuffmanTree h, int delta) { - Block8x8F* b = this.pointers.Block; + Block8x8* b = this.pointers.Block; // Refining a DC component is trivial. if (this.zigStart == 0) @@ -512,21 +517,21 @@ namespace ImageSharp.Formats.Jpg } bool bit; - DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); - if (!bp.CheckEOFEnsureNoError(errorCode)) + bp.DecodeBitUnsafe(out bit); + if (!bp.CheckEOFEnsureNoError()) { return; } if (bit) { - int stuff = (int)Block8x8F.GetScalarAt(b, 0); + int stuff = (int)Block8x8.GetScalarAt(b, 0); // int stuff = (int)b[0]; stuff |= delta; // b[0] = stuff; - Block8x8F.SetScalarAt(b, 0, stuff); + Block8x8.SetScalarAt(b, 0, (short)stuff); } return; @@ -542,8 +547,8 @@ namespace ImageSharp.Formats.Jpg int z = 0; int val; - DecoderErrorCode errorCode = bp.DecodeHuffmanUnsafe(ref h, out val); - if (!bp.CheckEOF(errorCode)) + bp.DecodeHuffmanUnsafe(ref h, out val); + if (!bp.CheckEOF()) { return; } @@ -559,8 +564,8 @@ namespace ImageSharp.Formats.Jpg this.eobRun = 1 << val0; if (val0 != 0) { - errorCode = this.DecodeEobRun(val0, ref bp); - if (!bp.CheckEOFEnsureNoError(errorCode)) + this.DecodeEobRun(val0, ref bp); + if (!bp.CheckEOFEnsureNoError()) { return; } @@ -574,8 +579,8 @@ namespace ImageSharp.Formats.Jpg z = delta; bool bit; - errorCode = bp.DecodeBitUnsafe(out bit); - if (!bp.CheckEOFEnsureNoError(errorCode)) + bp.DecodeBitUnsafe(out bit); + if (!bp.CheckEOFEnsureNoError()) { return; } @@ -596,20 +601,15 @@ namespace ImageSharp.Formats.Jpg } zig = this.RefineNonZeroes(ref bp, zig, val0, delta); - if (bp.UnexpectedEndOfStreamReached) + if (bp.ReachedEOF) { return; } - if (zig > this.zigEnd) - { - throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); - } - - if (z != 0) + if (z != 0 && zig <= this.zigEnd) { // b[Unzig[zig]] = z; - Block8x8F.SetScalarAt(b, this.pointers.Unzig[zig], z); + Block8x8.SetScalarAt(b, this.pointers.Unzig[zig], (short)z); } } } @@ -632,11 +632,11 @@ namespace ImageSharp.Formats.Jpg /// The private int RefineNonZeroes(ref InputProcessor bp, int zig, int nz, int delta) { - Block8x8F* b = this.pointers.Block; + Block8x8* b = this.pointers.Block; for (; zig <= this.zigEnd; zig++) { int u = this.pointers.Unzig[zig]; - float bu = Block8x8F.GetScalarAt(b, u); + int bu = Block8x8.GetScalarAt(b, u); // TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary? if (bu == 0) @@ -651,8 +651,8 @@ namespace ImageSharp.Formats.Jpg } bool bit; - DecoderErrorCode errorCode = bp.DecodeBitUnsafe(out bit); - if (!bp.CheckEOFEnsureNoError(errorCode)) + bp.DecodeBitUnsafe(out bit); + if (bp.HasError) { return int.MinValue; } @@ -662,16 +662,9 @@ namespace ImageSharp.Formats.Jpg continue; } - if (bu >= 0) - { - // b[u] += delta; - Block8x8F.SetScalarAt(b, u, bu + delta); - } - else - { - // b[u] -= delta; - Block8x8F.SetScalarAt(b, u, bu - delta); - } + int val = bu >= 0 ? bu + delta : bu - delta; + + Block8x8.SetScalarAt(b, u, (short)val); } return zig; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs similarity index 81% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs index 3875cc12f..23fcda296 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffIndex.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffIndex.cs @@ -1,8 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// Enumerates the Huffman tables diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs similarity index 92% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs index d0003b919..2fb01c5c8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanLut.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanLut.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// A compiled look-up table representation of a huffmanSpec. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs similarity index 97% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs index a0eea6e71..8e40cb368 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanSpec.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/HuffmanSpec.cs @@ -1,8 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// The Huffman encoding specifications. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs similarity index 71% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs index 5a469e0e9..459d29f91 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/QuantIndex.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/QuantIndex.cs @@ -1,8 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder { /// /// Enumerates the quantization tables diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs similarity index 95% rename from src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs index 94173d3e4..02bd451b9 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Encoder/RgbToYCbCrTables.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder +{ /// /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. /// Methods to build the tables are based on libjpeg implementation. diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs similarity index 94% rename from src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index d2b7d2d7c..984fb828c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -1,19 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System.Buffers; +using System.IO; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Encoder; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort { - using System.Buffers; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; - using ImageSharp.Formats.Jpg.Components; - using ImageSharp.PixelFormats; - /// /// Image encoder for writing an image to a stream as a jpeg. /// @@ -57,7 +58,7 @@ namespace ImageSharp.Formats /// private static readonly byte[] SosHeaderYCbCr = { - JpegConstants.Markers.XFF, JpegConstants.Markers.SOS, + OrigJpegConstants.Markers.XFF, OrigJpegConstants.Markers.SOS, // Marker 0x00, 0x0c, @@ -196,7 +197,7 @@ namespace ImageSharp.Formats Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - ushort max = JpegConstants.MaxLength; + ushort max = OrigJpegConstants.MaxLength; if (image.Width >= max || image.Height >= max) { throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); @@ -245,8 +246,8 @@ namespace ImageSharp.Formats } // Write the End Of Image marker. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.EOI; + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.EOI; stream.Write(this.buffer, 0, 2); stream.Flush(); } @@ -261,7 +262,7 @@ namespace ImageSharp.Formats private static void WriteDataToDqt(byte[] dqt, ref int offset, QuantIndex i, ref Block8x8F quant) { dqt[offset++] = (byte)i; - for (int j = 0; j < Block8x8F.ScalarCount; j++) + for (int j = 0; j < Block8x8F.Size; j++) { dqt[offset++] = (byte)quant[j]; } @@ -275,7 +276,7 @@ namespace ImageSharp.Formats /// The quantization table. private static void InitQuantizationTable(int i, int scale, ref Block8x8F quant) { - for (int j = 0; j < Block8x8F.ScalarCount; j++) + for (int j = 0; j < Block8x8F.Size; j++) { int x = UnscaledQuant[i, j]; x = ((x * scale) + 50) / 100; @@ -507,12 +508,12 @@ namespace ImageSharp.Formats private void WriteApplicationHeader(short horizontalResolution, short verticalResolution) { // Write the start of image marker. Markers are always prefixed with with 0xff. - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.SOI; + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.SOI; // Write the JFIF headers - this.buffer[2] = JpegConstants.Markers.XFF; - this.buffer[3] = JpegConstants.Markers.APP0; // Application Marker + this.buffer[2] = OrigJpegConstants.Markers.XFF; + this.buffer[3] = OrigJpegConstants.Markers.APP0; // Application Marker this.buffer[4] = 0x00; this.buffer[5] = 0x10; this.buffer[6] = 0x4a; // J @@ -561,7 +562,7 @@ namespace ImageSharp.Formats Block8x8F* quant, int* unzigPtr) { - DCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); + FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); Block8x8F.UnzigDivRound(tempDest1, tempDest2, quant, unzigPtr); float* unziggedDestPtr = (float*)tempDest2; @@ -575,7 +576,7 @@ namespace ImageSharp.Formats HuffIndex h = (HuffIndex)((2 * (int)index) + 1); int runLength = 0; - for (int zig = 1; zig < Block8x8F.ScalarCount; zig++) + for (int zig = 1; zig < Block8x8F.Size; zig++) { int ac = (int)unziggedDestPtr[zig]; @@ -626,7 +627,7 @@ namespace ImageSharp.Formats markerlen += 1 + 16 + s.Values.Length; } - this.WriteMarkerHeader(JpegConstants.Markers.DHT, markerlen); + this.WriteMarkerHeader(OrigJpegConstants.Markers.DHT, markerlen); for (int i = 0; i < specs.Length; i++) { HuffmanSpec spec = specs[i]; @@ -659,12 +660,12 @@ namespace ImageSharp.Formats private void WriteDefineQuantizationTables() { // Marker + quantization table lengths - int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.ScalarCount)); - this.WriteMarkerHeader(JpegConstants.Markers.DQT, markerlen); + int markerlen = 2 + (QuantizationTableCount * (1 + Block8x8F.Size)); + this.WriteMarkerHeader(OrigJpegConstants.Markers.DQT, markerlen); // Loop through and collect the tables as one array. // This allows us to reduce the number of writes to the stream. - int dqtCount = (QuantizationTableCount * Block8x8F.ScalarCount) + QuantizationTableCount; + int dqtCount = (QuantizationTableCount * Block8x8F.Size) + QuantizationTableCount; byte[] dqt = ArrayPool.Shared.Rent(dqtCount); int offset = 0; @@ -698,8 +699,8 @@ namespace ImageSharp.Formats int length = data.Length + 2; - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.APP1; // Application Marker + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.APP1; // Application Marker this.buffer[2] = (byte)((length >> 8) & 0xFF); this.buffer[3] = (byte)(length & 0xFF); @@ -757,8 +758,8 @@ namespace ImageSharp.Formats dataLength -= length; - this.buffer[0] = JpegConstants.Markers.XFF; - this.buffer[1] = JpegConstants.Markers.APP2; // Application Marker + this.buffer[0] = OrigJpegConstants.Markers.XFF; + this.buffer[1] = OrigJpegConstants.Markers.APP2; // Application Marker int markerLength = length + 16; this.buffer[2] = (byte)((markerLength >> 8) & 0xFF); this.buffer[3] = (byte)(markerLength & 0xFF); @@ -830,7 +831,7 @@ namespace ImageSharp.Formats // Length (high byte, low byte), 8 + components * 3. int markerlen = 8 + (3 * componentCount); - this.WriteMarkerHeader(JpegConstants.Markers.SOF0, markerlen); + this.WriteMarkerHeader(OrigJpegConstants.Markers.SOF0, markerlen); this.buffer[0] = 8; // Data Precision. 8 for now, 12 and 16 bit jpegs not supported this.buffer[1] = (byte)(height >> 8); this.buffer[2] = (byte)(height & 0xff); // (2 bytes, Hi-Lo), must be > 0 if DNL not supported @@ -973,7 +974,7 @@ namespace ImageSharp.Formats private void WriteMarkerHeader(byte marker, int length) { // Markers are always prefixed with with 0xff. - this.buffer[0] = JpegConstants.Markers.XFF; + this.buffer[0] = OrigJpegConstants.Markers.XFF; this.buffer[1] = marker; this.buffer[2] = (byte)(length >> 8); this.buffer[3] = (byte)(length & 0xff); diff --git a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs similarity index 97% rename from src/ImageSharp/Formats/Jpeg/JpegConstants.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs index 99c0399dc..f38c72820 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort +{ /// /// Defines jpeg constants defined in the specification. /// - internal static class JpegConstants + internal static class OrigJpegConstants { /// /// The maximum allowable length in each dimension of a jpeg image. diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs new file mode 100644 index 000000000..13be70e30 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort +{ + /// + /// Image decoder for generating an image out of a jpg stream. + /// + internal sealed class OrigJpegDecoder : IImageDecoder, IJpegDecoderOptions + { + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; set; } + + /// + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + using (var decoder = new OrigJpegDecoderCore(configuration, this)) + { + return decoder.Decode(stream); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs new file mode 100644 index 000000000..8369e9236 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -0,0 +1,823 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; + +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort +{ + /// + /// + /// Performs the jpeg decoding operation. + /// + internal sealed unsafe class OrigJpegDecoderCore : IRawJpegData + { + /// + /// The maximum number of color components + /// + public const int MaxComponents = 4; + + /// + /// The maximum number of quantization tables + /// + public const int MaxTq = 3; + + // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P +#pragma warning disable SA1401 // FieldsMustBePrivate + + /// + /// Encapsulates stream reading and processing data and operations for . + /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s + /// + public InputProcessor InputProcessor; +#pragma warning restore SA401 + + /// + /// The global configuration + /// + private readonly Configuration configuration; + + /// + /// The App14 marker color-space + /// + private byte adobeTransform; + + /// + /// Whether the image is in CMYK format with an App14 marker + /// + private bool adobeTransformValid; + + /// + /// The horizontal resolution. Calculated if the image has a JFIF header. + /// + private short horizontalResolution; + + /// + /// Whether the image has a JFIF header + /// + private bool isJfif; + + /// + /// Whether the image has a EXIF header + /// + private bool isExif; + + /// + /// The vertical resolution. Calculated if the image has a JFIF header. + /// + private short verticalResolution; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public OrigJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) + { + this.IgnoreMetadata = options.IgnoreMetadata; + this.configuration = configuration ?? Configuration.Default; + this.HuffmanTrees = OrigHuffmanTree.CreateHuffmanTrees(); + this.QuantizationTables = new Block8x8F[MaxTq + 1]; + this.Temp = new byte[2 * Block8x8F.Size]; + } + + /// + public JpegColorSpace ColorSpace { get; private set; } + + /// + /// Gets the component array + /// + public OrigComponent[] Components { get; private set; } + + /// + /// Gets the huffman trees + /// + public OrigHuffmanTree[] HuffmanTrees { get; } + + /// + public Block8x8F[] QuantizationTables { get; } + + /// + /// Gets the temporary buffer used to store bytes read from the stream. + /// TODO: Should be stack allocated, fixed sized buffer! + /// + public byte[] Temp { get; } + + /// + public Size ImageSizeInPixels { get; private set; } + + /// + /// Gets the number of MCU blocks in the image as . + /// + public Size ImageSizeInMCU { get; private set; } + + /// + public int ComponentCount { get; private set; } + + IEnumerable IRawJpegData.Components => this.Components; + + /// + /// Gets the image height + /// + public int ImageHeight => this.ImageSizeInPixels.Height; + + /// + /// Gets the image width + /// + public int ImageWidth => this.ImageSizeInPixels.Width; + + /// + /// Gets the input stream. + /// + public Stream InputStream { get; private set; } + + /// + /// Gets a value indicating whether the image is interlaced (progressive) + /// + public bool IsProgressive { get; private set; } + + /// + /// Gets the restart interval + /// + public int RestartInterval { get; private set; } + + /// + /// Gets the number of MCU-s (Minimum Coded Units) in the image along the X axis + /// + public int MCUCountX => this.ImageSizeInMCU.Width; + + /// + /// Gets the number of MCU-s (Minimum Coded Units) in the image along the Y axis + /// + public int MCUCountY => this.ImageSizeInMCU.Height; + + /// + /// Gets the the total number of MCU-s (Minimum Coded Units) in the image. + /// + public int TotalMCUCount => this.MCUCountX * this.MCUCountY; + + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; } + + /// + /// Gets the decoded by this decoder instance. + /// + public ImageMetaData MetaData { get; private set; } + + /// + /// Decodes the image from the specified and sets + /// the data to image. + /// + /// The pixel format. + /// The stream, where the image should be. + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : struct, IPixel + { + this.ParseStream(stream); + + return this.PostProcessIntoImage(); + } + + /// + public void Dispose() + { + for (int i = 0; i < this.HuffmanTrees.Length; i++) + { + this.HuffmanTrees[i].Dispose(); + } + + if (this.Components != null) + { + foreach (OrigComponent component in this.Components) + { + component.Dispose(); + } + } + + this.InputProcessor.Dispose(); + } + + /// + /// Read metadata from stream and read the blocks in the scans into . + /// + /// The stream + /// Whether to decode metadata only. + public void ParseStream(Stream stream, bool metadataOnly = false) + { + this.MetaData = new ImageMetaData(); + this.InputStream = stream; + this.InputProcessor = new InputProcessor(stream, this.Temp); + + // Check for the Start Of Image marker. + this.InputProcessor.ReadFull(this.Temp, 0, 2); + if (this.Temp[0] != OrigJpegConstants.Markers.XFF || this.Temp[1] != OrigJpegConstants.Markers.SOI) + { + throw new ImageFormatException("Missing SOI marker."); + } + + // Process the remaining segments until the End Of Image marker. + bool processBytes = true; + + // we can't currently short circute progressive images so don't try. + while (processBytes) + { + this.InputProcessor.ReadFull(this.Temp, 0, 2); + while (this.Temp[0] != 0xff) + { + // Strictly speaking, this is a format error. However, libjpeg is + // liberal in what it accepts. As of version 9, next_marker in + // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and + // continues to decode the stream. Even before next_marker sees + // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many + // bytes as it can, possibly past the end of a scan's data. It + // effectively puts back any markers that it overscanned (e.g. an + // "\xff\xd9" EOI marker), but it does not put back non-marker data, + // and thus it can silently ignore a small number of extraneous + // non-marker bytes before next_marker has a chance to see them (and + // print a warning). + // We are therefore also liberal in what we accept. Extraneous data + // is silently ignore + // This is similar to, but not exactly the same as, the restart + // mechanism within a scan (the RST[0-7] markers). + // Note that extraneous 0xff bytes in e.g. SOS data are escaped as + // "\xff\x00", and so are detected a little further down below. + this.Temp[0] = this.Temp[1]; + this.Temp[1] = this.InputProcessor.ReadByte(); + } + + byte marker = this.Temp[1]; + if (marker == 0) + { + // Treat "\xff\x00" as extraneous data. + continue; + } + + while (marker == 0xff) + { + // Section B.1.1.2 says, "Any marker may optionally be preceded by any + // number of fill bytes, which are bytes assigned code X'FF'". + marker = this.InputProcessor.ReadByte(); + } + + // End Of Image. + if (marker == OrigJpegConstants.Markers.EOI) + { + break; + } + + if (marker >= OrigJpegConstants.Markers.RST0 && marker <= OrigJpegConstants.Markers.RST7) + { + // Figures B.2 and B.16 of the specification suggest that restart markers should + // only occur between Entropy Coded Segments and not after the final ECS. + // However, some encoders may generate incorrect JPEGs with a final restart + // marker. That restart marker will be seen here instead of inside the ProcessSOS + // method, and is ignored as a harmless error. Restart markers have no extra data, + // so we check for this before we read the 16-bit length of the segment. + continue; + } + + // Read the 16-bit length of the segment. The value includes the 2 bytes for the + // length itself, so we subtract 2 to get the number of remaining bytes. + this.InputProcessor.ReadFull(this.Temp, 0, 2); + int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; + if (remaining < 0) + { + throw new ImageFormatException("Short segment length."); + } + + switch (marker) + { + case OrigJpegConstants.Markers.SOF0: + case OrigJpegConstants.Markers.SOF1: + case OrigJpegConstants.Markers.SOF2: + this.IsProgressive = marker == OrigJpegConstants.Markers.SOF2; + this.ProcessStartOfFrameMarker(remaining); + if (metadataOnly && this.isJfif) + { + return; + } + + break; + case OrigJpegConstants.Markers.DHT: + if (metadataOnly) + { + this.InputProcessor.Skip(remaining); + } + else + { + this.ProcessDefineHuffmanTablesMarker(remaining); + } + + break; + case OrigJpegConstants.Markers.DQT: + if (metadataOnly) + { + this.InputProcessor.Skip(remaining); + } + else + { + this.ProcessDqt(remaining); + } + + break; + case OrigJpegConstants.Markers.SOS: + if (metadataOnly) + { + return; + } + + // when this is a progressive image this gets called a number of times + // need to know how many times this should be called in total. + this.ProcessStartOfScan(remaining); + if (this.InputProcessor.ReachedEOF || !this.IsProgressive) + { + // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data. + processBytes = false; + } + + break; + case OrigJpegConstants.Markers.DRI: + if (metadataOnly) + { + this.InputProcessor.Skip(remaining); + } + else + { + this.ProcessDefineRestartIntervalMarker(remaining); + } + + break; + case OrigJpegConstants.Markers.APP0: + this.ProcessApplicationHeader(remaining); + break; + case OrigJpegConstants.Markers.APP1: + this.ProcessApp1Marker(remaining); + break; + case OrigJpegConstants.Markers.APP2: + this.ProcessApp2Marker(remaining); + break; + case OrigJpegConstants.Markers.APP14: + this.ProcessApp14Marker(remaining); + break; + default: + if ((marker >= OrigJpegConstants.Markers.APP0 && marker <= OrigJpegConstants.Markers.APP15) + || marker == OrigJpegConstants.Markers.COM) + { + this.InputProcessor.Skip(remaining); + } + else if (marker < OrigJpegConstants.Markers.SOF0) + { + // See Table B.1 "Marker code assignments". + throw new ImageFormatException("Unknown marker"); + } + else + { + throw new ImageFormatException("Unknown marker"); + } + + break; + } + } + + this.InitDerivedMetaDataProperties(); + } + + /// + /// Processes the SOS (Start of scan marker). + /// + /// The remaining bytes in the segment block. + /// + /// Missing SOF Marker + /// SOS has wrong length + /// + private void ProcessStartOfScan(int remaining) + { + var scan = default(OrigJpegScanDecoder); + OrigJpegScanDecoder.InitStreamReading(&scan, this, remaining); + this.InputProcessor.Bits = default(Bits); + scan.DecodeBlocks(this); + } + + /// + /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. + /// + private void InitDerivedMetaDataProperties() + { + if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) + { + this.MetaData.HorizontalResolution = this.horizontalResolution; + this.MetaData.VerticalResolution = this.verticalResolution; + } + else if (this.isExif) + { + ExifValue horizontal = this.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + ExifValue vertical = this.MetaData.ExifProfile.GetValue(ExifTag.YResolution); + double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; + double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; + + if (horizontalValue > 0 && verticalValue > 0) + { + this.MetaData.HorizontalResolution = horizontalValue; + this.MetaData.VerticalResolution = verticalValue; + } + } + } + + /// + /// Returns a value indicating whether the image in an RGB image. + /// + /// + /// The . + /// + private bool IsRGB() + { + if (this.isJfif) + { + return false; + } + + if (this.adobeTransformValid && this.adobeTransform == OrigJpegConstants.Adobe.ColorTransformUnknown) + { + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr. + return true; + } + + return this.Components[0].Identifier == 'R' && this.Components[1].Identifier == 'G' + && this.Components[2].Identifier == 'B'; + } + + /// + /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters. + /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not + /// deleted by default when deleting all metadata because it may affect the appearance of the image. + /// + /// The remaining number of bytes in the stream. + private void ProcessApp14Marker(int remaining) + { + if (remaining < 12) + { + this.InputProcessor.Skip(remaining); + return; + } + + this.InputProcessor.ReadFull(this.Temp, 0, 12); + remaining -= 12; + + if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b' + && this.Temp[4] == 'e') + { + this.adobeTransformValid = true; + this.adobeTransform = this.Temp[11]; + } + + if (remaining > 0) + { + this.InputProcessor.Skip(remaining); + } + } + + /// + /// Processes the App1 marker retrieving any stored metadata + /// + /// The remaining bytes in the segment block. + private void ProcessApp1Marker(int remaining) + { + if (remaining < 6 || this.IgnoreMetadata) + { + this.InputProcessor.Skip(remaining); + return; + } + + byte[] profile = new byte[remaining]; + this.InputProcessor.ReadFull(profile, 0, remaining); + + if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0' + && profile[5] == '\0') + { + this.isExif = true; + this.MetaData.ExifProfile = new ExifProfile(profile); + } + } + + /// + /// Processes the App2 marker retrieving any stored ICC profile information + /// + /// The remaining bytes in the segment block. + private void ProcessApp2Marker(int remaining) + { + // Length is 14 though we only need to check 12. + const int Icclength = 14; + if (remaining < Icclength || this.IgnoreMetadata) + { + this.InputProcessor.Skip(remaining); + return; + } + + byte[] identifier = new byte[Icclength]; + this.InputProcessor.ReadFull(identifier, 0, Icclength); + remaining -= Icclength; // we have read it by this point + + if (identifier[0] == 'I' && identifier[1] == 'C' && identifier[2] == 'C' && identifier[3] == '_' + && identifier[4] == 'P' && identifier[5] == 'R' && identifier[6] == 'O' && identifier[7] == 'F' + && identifier[8] == 'I' && identifier[9] == 'L' && identifier[10] == 'E' && identifier[11] == '\0') + { + byte[] profile = new byte[remaining]; + this.InputProcessor.ReadFull(profile, 0, remaining); + + if (this.MetaData.IccProfile == null) + { + this.MetaData.IccProfile = new IccProfile(profile); + } + else + { + this.MetaData.IccProfile.Extend(profile); + } + } + else + { + // not an ICC profile we can handle read the remaining so we can carry on and ignore this. + this.InputProcessor.Skip(remaining); + } + } + + /// + /// Processes the application header containing the JFIF identifier plus extra data. + /// + /// The remaining bytes in the segment block. + private void ProcessApplicationHeader(int remaining) + { + if (remaining < 5) + { + this.InputProcessor.Skip(remaining); + return; + } + + this.InputProcessor.ReadFull(this.Temp, 0, 13); + remaining -= 13; + + // TODO: We should be using constants for this. + this.isJfif = this.Temp[0] == 'J' && this.Temp[1] == 'F' && this.Temp[2] == 'I' && this.Temp[3] == 'F' + && this.Temp[4] == '\x00'; + + if (this.isJfif) + { + this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[8] << 8)); + this.verticalResolution = (short)(this.Temp[11] + (this.Temp[10] << 8)); + } + + if (remaining > 0) + { + this.InputProcessor.Skip(remaining); + } + } + + /// + /// Processes a Define Huffman Table marker, and initializes a huffman + /// struct from its contents. Specified in section B.2.4.2. + /// + /// The remaining bytes in the segment block. + private void ProcessDefineHuffmanTablesMarker(int remaining) + { + while (remaining > 0) + { + if (remaining < 17) + { + throw new ImageFormatException("DHT has wrong length"); + } + + this.InputProcessor.ReadFull(this.Temp, 0, 17); + + int tc = this.Temp[0] >> 4; + if (tc > OrigHuffmanTree.MaxTc) + { + throw new ImageFormatException("Bad Tc value"); + } + + int th = this.Temp[0] & 0x0f; + if (th > OrigHuffmanTree.MaxTh || (!this.IsProgressive && (th > 1))) + { + throw new ImageFormatException("Bad Th value"); + } + + int huffTreeIndex = (tc * OrigHuffmanTree.ThRowSize) + th; + this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( + ref this.InputProcessor, + this.Temp, + ref remaining); + } + } + + /// + /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in + /// macroblocks + /// + /// The remaining bytes in the segment block. + private void ProcessDefineRestartIntervalMarker(int remaining) + { + if (remaining != 2) + { + throw new ImageFormatException("DRI has wrong length"); + } + + this.InputProcessor.ReadFull(this.Temp, 0, remaining); + this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; + } + + /// + /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. + /// + /// The remaining bytes in the segment block. + /// + /// Thrown if the tables do not match the header + /// + private void ProcessDqt(int remaining) + { + while (remaining > 0) + { + bool done = false; + + remaining--; + byte x = this.InputProcessor.ReadByte(); + int tq = x & 0x0F; + if (tq > MaxTq) + { + throw new ImageFormatException("Bad Tq value"); + } + + switch (x >> 4) + { + case 0: + if (remaining < Block8x8F.Size) + { + done = true; + break; + } + + remaining -= Block8x8F.Size; + this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.Size); + + for (int i = 0; i < Block8x8F.Size; i++) + { + this.QuantizationTables[tq][i] = this.Temp[i]; + } + + break; + case 1: + if (remaining < 2 * Block8x8F.Size) + { + done = true; + break; + } + + remaining -= 2 * Block8x8F.Size; + this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.Size); + + for (int i = 0; i < Block8x8F.Size; i++) + { + this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; + } + + break; + default: + throw new ImageFormatException("Bad Pq value"); + } + + if (done) + { + break; + } + } + + if (remaining != 0) + { + throw new ImageFormatException("DQT has wrong length"); + } + } + + /// + /// Processes the Start of Frame marker. Specified in section B.2.2. + /// + /// The remaining bytes in the segment block. + private void ProcessStartOfFrameMarker(int remaining) + { + if (this.ComponentCount != 0) + { + throw new ImageFormatException("Multiple SOF markers"); + } + + switch (remaining) + { + case 6 + (3 * 1): // Grayscale image. + this.ComponentCount = 1; + break; + case 6 + (3 * 3): // YCbCr or RGB image. + this.ComponentCount = 3; + break; + case 6 + (3 * 4): // YCbCrK or CMYK image. + this.ComponentCount = 4; + break; + default: + throw new ImageFormatException("Incorrect number of components"); + } + + this.InputProcessor.ReadFull(this.Temp, 0, remaining); + + // We only support 8-bit precision. + if (this.Temp[0] != 8) + { + throw new ImageFormatException("Only 8-Bit precision supported."); + } + + int height = (this.Temp[1] << 8) + this.Temp[2]; + int width = (this.Temp[3] << 8) + this.Temp[4]; + + this.ImageSizeInPixels = new Size(width, height); + + if (this.Temp[5] != this.ComponentCount) + { + throw new ImageFormatException("SOF has wrong length"); + } + + this.Components = new OrigComponent[this.ComponentCount]; + + for (int i = 0; i < this.ComponentCount; i++) + { + byte componentIdentifier = this.Temp[6 + (3 * i)]; + var component = new OrigComponent(componentIdentifier, i); + component.InitializeCoreData(this); + this.Components[i] = component; + } + + int h0 = this.Components[0].HorizontalSamplingFactor; + int v0 = this.Components[0].VerticalSamplingFactor; + + this.ImageSizeInMCU = this.ImageSizeInPixels.DivideRoundUp(8 * h0, 8 * v0); + + foreach (OrigComponent component in this.Components) + { + component.InitializeDerivedData(this); + } + + this.ColorSpace = this.DeduceJpegColorSpace(); + } + + private JpegColorSpace DeduceJpegColorSpace() + { + switch (this.ComponentCount) + { + case 1: return JpegColorSpace.GrayScale; + case 3: return this.IsRGB() ? JpegColorSpace.RGB : JpegColorSpace.YCbCr; + case 4: + + if (!this.adobeTransformValid) + { + throw new ImageFormatException( + "Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata"); + } + + // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe + // See https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html + // TODO: YCbCrA? + if (this.adobeTransform == OrigJpegConstants.Adobe.ColorTransformYcck) + { + return JpegColorSpace.Ycck; + } + else if (this.adobeTransform == OrigJpegConstants.Adobe.ColorTransformUnknown) + { + // Assume CMYK + return JpegColorSpace.Cmyk; + } + + goto default; + + default: + throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces."); + } + } + + private Image PostProcessIntoImage() + where TPixel : struct, IPixel + { + using (var postProcessor = new JpegImagePostProcessor(this)) + { + var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); + postProcessor.PostProcess(image.Frames.RootFrame); + return image; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs similarity index 89% rename from src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs rename to src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs index afb8e0700..01ed5063b 100644 --- a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Utils/OrigJpegUtils.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils +{ /// /// Jpeg specific utilities and extension methods /// - internal static unsafe class JpegUtils + internal static class OrigJpegUtils { /// /// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image. diff --git a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs index 6830e2e4a..880a7f7a3 100644 --- a/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/IJpegDecoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Image decoder for generating an image out of a jpg stream. /// diff --git a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs index 947c98ee2..a84652cef 100644 --- a/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs +++ b/src/ImageSharp/Formats/Jpeg/IJpegEncoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Encoder for writing the data image to a stream in jpeg format. /// diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index 8fbf9e5a7..9cd7b3a8b 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,14 +22,9 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsJpeg(this Image source, Stream stream) + public static void SaveAsJpeg(this Image source, Stream stream) where TPixel : struct, IPixel - { - return SaveAsJpeg(source, stream, null); - } + => SaveAsJpeg(source, stream, null); /// /// Saves the image to the given stream with the jpeg format. @@ -41,16 +34,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) + public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) where TPixel : struct, IPixel - { - encoder = encoder ?? new JpegEncoder(); - encoder.Encode(source, stream); - - return source; - } + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Jpeg)); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs index bb8c4e83f..1ab509339 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Jpeg { /// /// Registers the image encoders, decoders and mime type detectors for the jpeg format. diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index b3caddeca..68f525305 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; +using System.IO; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Image decoder for generating an image out of a jpg stream. /// @@ -25,12 +23,12 @@ namespace ImageSharp.Formats public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { - Guard.NotNull(stream, "stream"); + Guard.NotNull(stream, nameof(stream)); - using (JpegDecoderCore decoder = new JpegDecoderCore(configuration, this)) + using (var decoder = new OrigJpegDecoderCore(configuration, this)) { return decoder.Decode(stream); } } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs deleted file mode 100644 index 0ce927e51..000000000 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ /dev/null @@ -1,1374 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - - using ImageSharp.Formats.Jpg; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - /// - /// Performs the jpeg decoding operation. - /// - internal sealed unsafe class JpegDecoderCore : IDisposable - { - /// - /// The maximum number of color components - /// - public const int MaxComponents = 4; - - /// - /// The maximum number of quantization tables - /// - public const int MaxTq = 3; - - // Complex value type field + mutable + available to other classes = the field MUST NOT be private :P -#pragma warning disable SA1401 // FieldsMustBePrivate - - /// - /// Encapsulates stream reading and processing data and operations for . - /// It's a value type for imporved data locality, and reduced number of CALLVIRT-s - /// - public InputProcessor InputProcessor; -#pragma warning restore SA401 - - /// - /// Lookup tables for converting YCbCr to Rgb - /// - private static YCbCrToRgbTables yCbCrToRgbTables = YCbCrToRgbTables.Create(); - - /// - /// The global configuration - /// - private readonly Configuration configuration; - - /// - /// The App14 marker color-space - /// - private byte adobeTransform; - - /// - /// Whether the image is in CMYK format with an App14 marker - /// - private bool adobeTransformValid; - - /// - /// The black image to decode to. - /// - private JpegPixelArea blackImage; - - /// - /// A grayscale image to decode to. - /// - private JpegPixelArea grayImage; - - /// - /// The horizontal resolution. Calculated if the image has a JFIF header. - /// - private short horizontalResolution; - - /// - /// Whether the image has a JFIF header - /// - private bool isJfif; - - /// - /// Whether the image has a EXIF header - /// - private bool isExif; - - /// - /// The vertical resolution. Calculated if the image has a JFIF header. - /// - private short verticalResolution; - - /// - /// The full color image to decode to. - /// - private YCbCrImage ycbcrImage; - - /// - /// Initializes a new instance of the class. - /// - /// The configuration. - /// The options. - public JpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) - { - this.IgnoreMetadata = options.IgnoreMetadata; - this.configuration = configuration ?? Configuration.Default; - this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees(); - this.QuantizationTables = new Block8x8F[MaxTq + 1]; - this.Temp = new byte[2 * Block8x8F.ScalarCount]; - this.ComponentArray = new Component[MaxComponents]; - this.DecodedBlocks = new Buffer[MaxComponents]; - } - - /// - /// Gets the component array - /// - public Component[] ComponentArray { get; } - - /// - /// Gets the huffman trees - /// - public HuffmanTree[] HuffmanTrees { get; } - - /// - /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. - /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. - /// This is done by . - /// When ==true, we are touching these blocks multiple times - each time we process a Scan. - /// - public Buffer[] DecodedBlocks { get; } - - /// - /// Gets the quantization tables, in zigzag order. - /// - public Block8x8F[] QuantizationTables { get; } - - /// - /// Gets the temporary buffer used to store bytes read from the stream. - /// TODO: Should be stack allocated, fixed sized buffer! - /// - public byte[] Temp { get; } - - /// - /// Gets the number of color components within the image. - /// - public int ComponentCount { get; private set; } - - /// - /// Gets the image height - /// - public int ImageHeight { get; private set; } - - /// - /// Gets the image width - /// - public int ImageWidth { get; private set; } - - /// - /// Gets the input stream. - /// - public Stream InputStream { get; private set; } - - /// - /// Gets a value indicating whether the image is interlaced (progressive) - /// - public bool IsProgressive { get; private set; } - - /// - /// Gets the restart interval - /// - public int RestartInterval { get; private set; } - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the X axis - /// - public int MCUCountX { get; private set; } - - /// - /// Gets the number of MCU-s (Minimum Coded Units) in the image along the Y axis - /// - public int MCUCountY { get; private set; } - - /// - /// Gets the the total number of MCU-s (Minimum Coded Units) in the image. - /// - public int TotalMCUCount => this.MCUCountX * this.MCUCountY; - - /// - /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. - /// - public bool IgnoreMetadata { get; private set; } - - /// - /// Decodes the image from the specified and sets - /// the data to image. - /// - /// The pixel format. - /// The stream, where the image should be. - /// The decoded image. - public Image Decode(Stream stream) - where TPixel : struct, IPixel - { - ImageMetaData metadata = new ImageMetaData(); - this.ProcessStream(metadata, stream, false); - this.ProcessBlocksIntoJpegImageChannels(); - Image image = this.ConvertJpegPixelsToImagePixels(metadata); - - return image; - } - - /// - /// Dispose - /// - public void Dispose() - { - for (int i = 0; i < this.HuffmanTrees.Length; i++) - { - this.HuffmanTrees[i].Dispose(); - } - - foreach (Buffer blockArray in this.DecodedBlocks) - { - blockArray?.Dispose(); - } - - this.ycbcrImage?.Dispose(); - this.InputProcessor.Dispose(); - this.grayImage.Pixels?.Dispose(); - this.blackImage.Pixels?.Dispose(); - } - - /// - /// Gets the representing the channel at a given component index - /// - /// The component index - /// The of the channel - public JpegPixelArea GetDestinationChannel(int compIndex) - { - if (this.ComponentCount == 1) - { - return this.grayImage; - } - else - { - switch (compIndex) - { - case 0: - return new JpegPixelArea(this.ycbcrImage.YChannel); - case 1: - return new JpegPixelArea(this.ycbcrImage.CbChannel); - case 2: - return new JpegPixelArea(this.ycbcrImage.CrChannel); - case 3: - return this.blackImage; - default: - throw new ImageFormatException("Too many components"); - } - } - } - - /// - /// Read metadata from stream and read the blocks in the scans into . - /// - /// The metadata - /// The stream - /// Whether to decode metadata only. - private void ProcessStream(ImageMetaData metadata, Stream stream, bool metadataOnly) - { - this.InputStream = stream; - this.InputProcessor = new InputProcessor(stream, this.Temp); - - // Check for the Start Of Image marker. - this.InputProcessor.ReadFull(this.Temp, 0, 2); - if (this.Temp[0] != JpegConstants.Markers.XFF || this.Temp[1] != JpegConstants.Markers.SOI) - { - throw new ImageFormatException("Missing SOI marker."); - } - - // Process the remaining segments until the End Of Image marker. - bool processBytes = true; - - // we can't currently short circute progressive images so don't try. - while (processBytes) - { - this.InputProcessor.ReadFull(this.Temp, 0, 2); - while (this.Temp[0] != 0xff) - { - // Strictly speaking, this is a format error. However, libjpeg is - // liberal in what it accepts. As of version 9, next_marker in - // jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and - // continues to decode the stream. Even before next_marker sees - // extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many - // bytes as it can, possibly past the end of a scan's data. It - // effectively puts back any markers that it overscanned (e.g. an - // "\xff\xd9" EOI marker), but it does not put back non-marker data, - // and thus it can silently ignore a small number of extraneous - // non-marker bytes before next_marker has a chance to see them (and - // print a warning). - // We are therefore also liberal in what we accept. Extraneous data - // is silently ignore - // This is similar to, but not exactly the same as, the restart - // mechanism within a scan (the RST[0-7] markers). - // Note that extraneous 0xff bytes in e.g. SOS data are escaped as - // "\xff\x00", and so are detected a little further down below. - this.Temp[0] = this.Temp[1]; - this.Temp[1] = this.InputProcessor.ReadByte(); - } - - byte marker = this.Temp[1]; - if (marker == 0) - { - // Treat "\xff\x00" as extraneous data. - continue; - } - - while (marker == 0xff) - { - // Section B.1.1.2 says, "Any marker may optionally be preceded by any - // number of fill bytes, which are bytes assigned code X'FF'". - marker = this.InputProcessor.ReadByte(); - } - - // End Of Image. - if (marker == JpegConstants.Markers.EOI) - { - break; - } - - if (marker >= JpegConstants.Markers.RST0 && marker <= JpegConstants.Markers.RST7) - { - // Figures B.2 and B.16 of the specification suggest that restart markers should - // only occur between Entropy Coded Segments and not after the final ECS. - // However, some encoders may generate incorrect JPEGs with a final restart - // marker. That restart marker will be seen here instead of inside the ProcessSOS - // method, and is ignored as a harmless error. Restart markers have no extra data, - // so we check for this before we read the 16-bit length of the segment. - continue; - } - - // Read the 16-bit length of the segment. The value includes the 2 bytes for the - // length itself, so we subtract 2 to get the number of remaining bytes. - this.InputProcessor.ReadFull(this.Temp, 0, 2); - int remaining = (this.Temp[0] << 8) + this.Temp[1] - 2; - if (remaining < 0) - { - throw new ImageFormatException("Short segment length."); - } - - switch (marker) - { - case JpegConstants.Markers.SOF0: - case JpegConstants.Markers.SOF1: - case JpegConstants.Markers.SOF2: - this.IsProgressive = marker == JpegConstants.Markers.SOF2; - this.ProcessStartOfFrameMarker(remaining); - if (metadataOnly && this.isJfif) - { - return; - } - - break; - case JpegConstants.Markers.DHT: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineHuffmanTablesMarker(remaining); - } - - break; - case JpegConstants.Markers.DQT: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDqt(remaining); - } - - break; - case JpegConstants.Markers.SOS: - if (metadataOnly) - { - return; - } - - // when this is a progressive image this gets called a number of times - // need to know how many times this should be called in total. - this.ProcessStartOfScan(remaining); - if (this.InputProcessor.UnexpectedEndOfStreamReached || !this.IsProgressive) - { - // if unexpeced EOF reached or this is not a progressive image we can stop processing bytes as we now have the image data. - processBytes = false; - } - - break; - case JpegConstants.Markers.DRI: - if (metadataOnly) - { - this.InputProcessor.Skip(remaining); - } - else - { - this.ProcessDefineRestartIntervalMarker(remaining); - } - - break; - case JpegConstants.Markers.APP0: - this.ProcessApplicationHeader(remaining); - break; - case JpegConstants.Markers.APP1: - this.ProcessApp1Marker(remaining, metadata); - break; - case JpegConstants.Markers.APP2: - this.ProcessApp2Marker(remaining, metadata); - break; - case JpegConstants.Markers.APP14: - this.ProcessApp14Marker(remaining); - break; - default: - if ((marker >= JpegConstants.Markers.APP0 && marker <= JpegConstants.Markers.APP15) - || marker == JpegConstants.Markers.COM) - { - this.InputProcessor.Skip(remaining); - } - else if (marker < JpegConstants.Markers.SOF0) - { - // See Table B.1 "Marker code assignments". - throw new ImageFormatException("Unknown marker"); - } - else - { - throw new ImageFormatException("Unknown marker"); - } - - break; - } - } - } - - /// - /// Processes the SOS (Start of scan marker). - /// - /// The remaining bytes in the segment block. - /// - /// Missing SOF Marker - /// SOS has wrong length - /// - private void ProcessStartOfScan(int remaining) - { - JpegScanDecoder scan = default(JpegScanDecoder); - JpegScanDecoder.InitStreamReading(&scan, this, remaining); - this.InputProcessor.Bits = default(Bits); - this.MakeImage(); - scan.DecodeBlocks(this); - } - - /// - /// Process the blocks in into Jpeg image channels ( and ) - /// are in a "raw" frequency-domain form. We need to apply IDCT, dequantization and unzigging to transform them into color-space blocks. - /// We can copy these blocks into -s afterwards. - /// - /// The pixel type - private void ProcessBlocksIntoJpegImageChannels() - where TPixel : struct, IPixel - { - Parallel.For( - 0, - this.ComponentCount, - componentIndex => - { - JpegBlockProcessor processor = default(JpegBlockProcessor); - JpegBlockProcessor.Init(&processor, componentIndex); - processor.ProcessAllBlocks(this); - }); - } - - /// - /// Convert the pixel data in and/or into pixels of - /// - /// The pixel type - /// The metadata for the image. - /// The decoded image. - private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) - where TPixel : struct, IPixel - { - Image image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); - - if (this.grayImage.IsInitialized) - { - this.ConvertFromGrayScale(image); - return image; - } - else if (this.ycbcrImage != null) - { - if (this.ComponentCount == 4) - { - if (!this.adobeTransformValid) - { - throw new ImageFormatException( - "Unknown color model: 4-component JPEG doesn't have Adobe APP14 metadata"); - } - - // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe - // See https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html - // TODO: YCbCrA? - if (this.adobeTransform == JpegConstants.Adobe.ColorTransformYcck) - { - this.ConvertFromYcck(image); - } - else if (this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) - { - // Assume CMYK - this.ConvertFromCmyk(image); - } - - return image; - } - - if (this.ComponentCount == 3) - { - if (this.IsRGB()) - { - this.ConvertFromRGB(image); - return image; - } - - this.ConvertFromYCbCr(image); - return image; - } - - throw new ImageFormatException("JpegDecoder only supports RGB, CMYK and Grayscale color spaces."); - } - else - { - throw new ImageFormatException("Missing SOS marker."); - } - } - - /// - /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header. - /// - /// The pixel format. - /// The image to assign the resolution to. - private void AssignResolution(Image image) - where TPixel : struct, IPixel - { - if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) - { - image.MetaData.HorizontalResolution = this.horizontalResolution; - image.MetaData.VerticalResolution = this.verticalResolution; - } - else if (this.isExif) - { - ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); - ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); - double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; - double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; - - if (horizontalValue > 0 && verticalValue > 0) - { - image.MetaData.HorizontalResolution = horizontalValue; - image.MetaData.VerticalResolution = verticalValue; - } - } - } - - /// - /// Converts the image from the original CMYK image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromCmyk(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < image.Width; x++) - { - byte cyan = this.ycbcrImage.YChannel[yo + x]; - byte magenta = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte yellow = this.ycbcrImage.CrChannel[co + (x / scale)]; - - TPixel packed = default(TPixel); - this.PackCmyk(ref packed, cyan, magenta, yellow, x, y); - pixels[x, y] = packed; - } - }); - } - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original grayscale image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromGrayScale(Image image) - where TPixel : struct, IPixel - { - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); - - int yoff = this.grayImage.GetRowOffset(y); - - for (int x = 0; x < image.Width; x++) - { - byte rgb = this.grayImage.Pixels[yoff + x]; - ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); - pixel.PackFromRgba32(new Rgba32(rgb, rgb, rgb, 255)); - } - }); - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original RBG image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromRGB(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); - - Rgba32 rgba = new Rgba32(0, 0, 0, 255); - - for (int x = 0; x < image.Width; x++) - { - rgba.R = this.ycbcrImage.YChannel[yo + x]; - rgba.G = this.ycbcrImage.CbChannel[co + (x / scale)]; - rgba.B = this.ycbcrImage.CrChannel[co + (x / scale)]; - - ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); - pixel.PackFromRgba32(rgba); - } - }); - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original YCbCr image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromYCbCr(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - // TODO. This Parallel loop doesn't give us the boost it should. - ref byte ycRef = ref this.ycbcrImage.YChannel[0]; - ref byte cbRef = ref this.ycbcrImage.CbChannel[0]; - ref byte crRef = ref this.ycbcrImage.CrChannel[0]; - fixed (YCbCrToRgbTables* tables = &yCbCrToRgbTables) - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < image.Width; x++) - { - int cOff = co + (x / scale); - byte yy = Unsafe.Add(ref ycRef, yo + x); - byte cb = Unsafe.Add(ref cbRef, cOff); - byte cr = Unsafe.Add(ref crRef, cOff); - - TPixel packed = default(TPixel); - YCbCrToRgbTables.Pack(ref packed, tables, yy, cb, cr); - pixels[x, y] = packed; - } - } - }); - } - - this.AssignResolution(image); - } - - /// - /// Converts the image from the original YCCK image pixels. - /// - /// The pixel format. - /// The image. - private void ConvertFromYcck(Image image) - where TPixel : struct, IPixel - { - int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - - Parallel.For( - 0, - image.Height, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); - - for (int x = 0; x < image.Width; x++) - { - byte yy = this.ycbcrImage.YChannel[yo + x]; - byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; - - ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); - this.PackYcck(ref pixel, yy, cb, cr, x, y); - } - }); - - this.AssignResolution(image); - } - - /// - /// Returns a value indicating whether the image in an RGB image. - /// - /// - /// The . - /// - private bool IsRGB() - { - if (this.isJfif) - { - return false; - } - - if (this.adobeTransformValid && this.adobeTransform == JpegConstants.Adobe.ColorTransformUnknown) - { - // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe - // says that 0 means Unknown (and in practice RGB) and 1 means YCbCr. - return true; - } - - return this.ComponentArray[0].Identifier == 'R' && this.ComponentArray[1].Identifier == 'G' - && this.ComponentArray[2].Identifier == 'B'; - } - - /// - /// Makes the image from the buffer. - /// - private void MakeImage() - { - if (this.grayImage.IsInitialized || this.ycbcrImage != null) - { - return; - } - - if (this.ComponentCount == 1) - { - Buffer2D buffer = Buffer2D.CreateClean(8 * this.MCUCountX, 8 * this.MCUCountY); - this.grayImage = new JpegPixelArea(buffer); - } - else - { - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; - int horizontalRatio = h0 / this.ComponentArray[1].HorizontalFactor; - int verticalRatio = v0 / this.ComponentArray[1].VerticalFactor; - - YCbCrImage.YCbCrSubsampleRatio ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; - switch ((horizontalRatio << 4) | verticalRatio) - { - case 0x11: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444; - break; - case 0x12: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440; - break; - case 0x21: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422; - break; - case 0x22: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420; - break; - case 0x41: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411; - break; - case 0x42: - ratio = YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410; - break; - } - - this.ycbcrImage = new YCbCrImage(8 * h0 * this.MCUCountX, 8 * v0 * this.MCUCountY, ratio); - - if (this.ComponentCount == 4) - { - int h3 = this.ComponentArray[3].HorizontalFactor; - int v3 = this.ComponentArray[3].VerticalFactor; - - Buffer2D buffer = Buffer2D.CreateClean(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY); - this.blackImage = new JpegPixelArea(buffer); - } - } - } - - /// - /// Optimized method to pack bytes to the image from the CMYK color space. - /// This is faster than implicit casting as it avoids double packing. - /// - /// The pixel format. - /// The packed pixel. - /// The cyan component. - /// The magenta component. - /// The yellow component. - /// The x-position within the image. - /// The y-position within the image. - private void PackCmyk(ref TPixel packed, byte c, byte m, byte y, int xx, int yy) - where TPixel : struct, IPixel - { - // Get keyline - float keyline = (255 - this.blackImage[xx, yy]) / 255F; - - // Convert back to RGB. CMY are not inverted - byte r = (byte)(((c / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - byte g = (byte)(((m / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - byte b = (byte)(((y / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - - packed.PackFromRgba32(new Rgba32(r, g, b)); - } - - /// - /// Optimized method to pack bytes to the image from the YCCK color space. - /// This is faster than implicit casting as it avoids double packing. - /// - /// The pixel format. - /// The packed pixel. - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - /// The x-position within the image. - /// The y-position within the image. - private void PackYcck(ref TPixel packed, byte y, byte cb, byte cr, int xx, int yy) - where TPixel : struct, IPixel - { - // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get - // CMY, and patch in the original K. The RGB to CMY inversion cancels - // out the 'Adobe inversion' described in the applyBlack doc comment - // above, so in practice, only the fourth channel (black) is inverted. - int ccb = cb - 128; - int ccr = cr - 128; - - // Speed up the algorithm by removing floating point calculation - // Scale by 65536, add .5F and truncate value. We use bit shifting to divide the result - int r0 = 91881 * ccr; // (1.402F * 65536) + .5F - int g0 = 22554 * ccb; // (0.34414F * 65536) + .5F - int g1 = 46802 * ccr; // (0.71414F * 65536) + .5F - int b0 = 116130 * ccb; // (1.772F * 65536) + .5F - - // First convert from YCbCr to CMY - float cyan = (y + (r0 >> 16)).Clamp(0, 255) / 255F; - float magenta = (byte)(y - (g0 >> 16) - (g1 >> 16)).Clamp(0, 255) / 255F; - float yellow = (byte)(y + (b0 >> 16)).Clamp(0, 255) / 255F; - - // Get keyline - float keyline = (255 - this.blackImage[xx, yy]) / 255F; - - // Convert back to RGB - byte r = (byte)(((1 - cyan) * (1 - keyline)).Clamp(0, 1) * 255); - byte g = (byte)(((1 - magenta) * (1 - keyline)).Clamp(0, 1) * 255); - byte b = (byte)(((1 - yellow) * (1 - keyline)).Clamp(0, 1) * 255); - - packed.PackFromRgba32(new Rgba32(r, g, b)); - } - - /// - /// Processes the "Adobe" APP14 segment stores image encoding information for DCT filters. - /// This segment may be copied or deleted as a block using the Extra "Adobe" tag, but note that it is not - /// deleted by default when deleting all metadata because it may affect the appearance of the image. - /// - /// The remaining number of bytes in the stream. - private void ProcessApp14Marker(int remaining) - { - if (remaining < 12) - { - this.InputProcessor.Skip(remaining); - return; - } - - this.InputProcessor.ReadFull(this.Temp, 0, 12); - remaining -= 12; - - if (this.Temp[0] == 'A' && this.Temp[1] == 'd' && this.Temp[2] == 'o' && this.Temp[3] == 'b' - && this.Temp[4] == 'e') - { - this.adobeTransformValid = true; - this.adobeTransform = this.Temp[11]; - } - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the App1 marker retrieving any stored metadata - /// - /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp1Marker(int remaining, ImageMetaData metadata) - { - if (remaining < 6 || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (profile[0] == 'E' && - profile[1] == 'x' && - profile[2] == 'i' && - profile[3] == 'f' && - profile[4] == '\0' && - profile[5] == '\0') - { - this.isExif = true; - metadata.ExifProfile = new ExifProfile(profile); - } - } - - /// - /// Processes the App2 marker retrieving any stored ICC profile information - /// - /// The remaining bytes in the segment block. - /// The image. - private void ProcessApp2Marker(int remaining, ImageMetaData metadata) - { - // Length is 14 though we only need to check 12. - const int Icclength = 14; - if (remaining < Icclength || this.IgnoreMetadata) - { - this.InputProcessor.Skip(remaining); - return; - } - - byte[] identifier = new byte[Icclength]; - this.InputProcessor.ReadFull(identifier, 0, Icclength); - remaining -= Icclength; // we have read it by this point - - if (identifier[0] == 'I' && - identifier[1] == 'C' && - identifier[2] == 'C' && - identifier[3] == '_' && - identifier[4] == 'P' && - identifier[5] == 'R' && - identifier[6] == 'O' && - identifier[7] == 'F' && - identifier[8] == 'I' && - identifier[9] == 'L' && - identifier[10] == 'E' && - identifier[11] == '\0') - { - byte[] profile = new byte[remaining]; - this.InputProcessor.ReadFull(profile, 0, remaining); - - if (metadata.IccProfile == null) - { - metadata.IccProfile = new IccProfile(profile); - } - else - { - metadata.IccProfile.Extend(profile); - } - } - else - { - // not an ICC profile we can handle read the remaining so we can carry on and ignore this. - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes the application header containing the JFIF identifier plus extra data. - /// - /// The remaining bytes in the segment block. - private void ProcessApplicationHeader(int remaining) - { - if (remaining < 5) - { - this.InputProcessor.Skip(remaining); - return; - } - - this.InputProcessor.ReadFull(this.Temp, 0, 13); - remaining -= 13; - - // TODO: We should be using constants for this. - this.isJfif = this.Temp[0] == 'J' && - this.Temp[1] == 'F' && - this.Temp[2] == 'I' && - this.Temp[3] == 'F' && - this.Temp[4] == '\x00'; - - if (this.isJfif) - { - this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[8] << 8)); - this.verticalResolution = (short)(this.Temp[11] + (this.Temp[10] << 8)); - } - - if (remaining > 0) - { - this.InputProcessor.Skip(remaining); - } - } - - /// - /// Processes a Define Huffman Table marker, and initializes a huffman - /// struct from its contents. Specified in section B.2.4.2. - /// - /// The remaining bytes in the segment block. - private void ProcessDefineHuffmanTablesMarker(int remaining) - { - while (remaining > 0) - { - if (remaining < 17) - { - throw new ImageFormatException("DHT has wrong length"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, 17); - - int tc = this.Temp[0] >> 4; - if (tc > HuffmanTree.MaxTc) - { - throw new ImageFormatException("Bad Tc value"); - } - - int th = this.Temp[0] & 0x0f; - if (th > HuffmanTree.MaxTh || (!this.IsProgressive && (th > 1))) - { - throw new ImageFormatException("Bad Th value"); - } - - int huffTreeIndex = (tc * HuffmanTree.ThRowSize) + th; - this.HuffmanTrees[huffTreeIndex].ProcessDefineHuffmanTablesMarkerLoop( - ref this.InputProcessor, - this.Temp, - ref remaining); - } - } - - /// - /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in - /// macroblocks - /// - /// The remaining bytes in the segment block. - private void ProcessDefineRestartIntervalMarker(int remaining) - { - if (remaining != 2) - { - throw new ImageFormatException("DRI has wrong length"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; - } - - /// - /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. - /// - /// The remaining bytes in the segment block. - /// - /// Thrown if the tables do not match the header - /// - private void ProcessDqt(int remaining) - { - while (remaining > 0) - { - bool done = false; - - remaining--; - byte x = this.InputProcessor.ReadByte(); - int tq = x & 0x0F; - if (tq > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - switch (x >> 4) - { - case 0: - if (remaining < Block8x8F.ScalarCount) - { - done = true; - break; - } - - remaining -= Block8x8F.ScalarCount; - this.InputProcessor.ReadFull(this.Temp, 0, Block8x8F.ScalarCount); - - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - this.QuantizationTables[tq][i] = this.Temp[i]; - } - - break; - case 1: - if (remaining < 2 * Block8x8F.ScalarCount) - { - done = true; - break; - } - - remaining -= 2 * Block8x8F.ScalarCount; - this.InputProcessor.ReadFull(this.Temp, 0, 2 * Block8x8F.ScalarCount); - - for (int i = 0; i < Block8x8F.ScalarCount; i++) - { - this.QuantizationTables[tq][i] = (this.Temp[2 * i] << 8) | this.Temp[(2 * i) + 1]; - } - - break; - default: - throw new ImageFormatException("Bad Pq value"); - } - - if (done) - { - break; - } - } - - if (remaining != 0) - { - throw new ImageFormatException("DQT has wrong length"); - } - } - - /// - /// Processes the Start of Frame marker. Specified in section B.2.2. - /// - /// The remaining bytes in the segment block. - private void ProcessStartOfFrameMarker(int remaining) - { - if (this.ComponentCount != 0) - { - throw new ImageFormatException("Multiple SOF markers"); - } - - switch (remaining) - { - case 6 + (3 * 1): // Grayscale image. - this.ComponentCount = 1; - break; - case 6 + (3 * 3): // YCbCr or RGB image. - this.ComponentCount = 3; - break; - case 6 + (3 * 4): // YCbCrK or CMYK image. - this.ComponentCount = 4; - break; - default: - throw new ImageFormatException("Incorrect number of components"); - } - - this.InputProcessor.ReadFull(this.Temp, 0, remaining); - - // We only support 8-bit precision. - if (this.Temp[0] != 8) - { - throw new ImageFormatException("Only 8-Bit precision supported."); - } - - this.ImageHeight = (this.Temp[1] << 8) + this.Temp[2]; - this.ImageWidth = (this.Temp[3] << 8) + this.Temp[4]; - if (this.Temp[5] != this.ComponentCount) - { - throw new ImageFormatException("SOF has wrong length"); - } - - for (int i = 0; i < this.ComponentCount; i++) - { - this.ComponentArray[i].Identifier = this.Temp[6 + (3 * i)]; - - // Section B.2.2 states that "the value of C_i shall be different from - // the values of C_1 through C_(i-1)". - for (int j = 0; j < i; j++) - { - if (this.ComponentArray[i].Identifier == this.ComponentArray[j].Identifier) - { - throw new ImageFormatException("Repeated component identifier"); - } - } - - this.ComponentArray[i].Selector = this.Temp[8 + (3 * i)]; - if (this.ComponentArray[i].Selector > MaxTq) - { - throw new ImageFormatException("Bad Tq value"); - } - - byte hv = this.Temp[7 + (3 * i)]; - int h = hv >> 4; - int v = hv & 0x0f; - if (h < 1 || h > 4 || v < 1 || v > 4) - { - throw new ImageFormatException("Unsupported Luma/chroma subsampling ratio"); - } - - if (h == 3 || v == 3) - { - throw new ImageFormatException("Lnsupported subsampling ratio"); - } - - switch (this.ComponentCount) - { - case 1: - - // If a JPEG image has only one component, section A.2 says "this data - // is non-interleaved by definition" and section A.2.2 says "[in this - // case...] the order of data units within a scan shall be left-to-right - // and top-to-bottom... regardless of the values of H_1 and V_1". Section - // 4.8.2 also says "[for non-interleaved data], the MCU is defined to be - // one data unit". Similarly, section A.1.1 explains that it is the ratio - // of H_i to max_j(H_j) that matters, and similarly for V. For grayscale - // images, H_1 is the maximum H_j for all components j, so that ratio is - // always 1. The component's (h, v) is effectively always (1, 1): even if - // the nominal (h, v) is (2, 1), a 20x5 image is encoded in three 8x8 - // MCUs, not two 16x8 MCUs. - h = 1; - v = 1; - break; - - case 3: - - // For YCbCr images, we only support 4:4:4, 4:4:0, 4:2:2, 4:2:0, - // 4:1:1 or 4:1:0 chroma subsampling ratios. This implies that the - // (h, v) values for the Y component are either (1, 1), (1, 2), - // (2, 1), (2, 2), (4, 1) or (4, 2), and the Y component's values - // must be a multiple of the Cb and Cr component's values. We also - // assume that the two chroma components have the same subsampling - // ratio. - switch (i) - { - case 0: - { - // Y. - // We have already verified, above, that h and v are both - // either 1, 2 or 4, so invalid (h, v) combinations are those - // with v == 4. - if (v == 4) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 1: - { - // Cb. - if (this.ComponentArray[0].HorizontalFactor % h != 0 - || this.ComponentArray[0].VerticalFactor % v != 0) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - case 2: - { - // Cr. - if (this.ComponentArray[1].HorizontalFactor != h - || this.ComponentArray[1].VerticalFactor != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - } - - break; - - case 4: - - // For 4-component images (either CMYK or YCbCrK), we only support two - // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. - // Theoretically, 4-component JPEG images could mix and match hv values - // but in practice, those two combinations are the only ones in use, - // and it simplifies the applyBlack code below if we can assume that: - // - for CMYK, the C and K channels have full samples, and if the M - // and Y channels subsample, they subsample both horizontally and - // vertically. - // - for YCbCrK, the Y and K channels have full samples. - switch (i) - { - case 0: - if (hv != 0x11 && hv != 0x22) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 1: - case 2: - if (hv != 0x11) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - case 3: - if (this.ComponentArray[0].HorizontalFactor != h - || this.ComponentArray[0].VerticalFactor != v) - { - throw new ImageFormatException("Unsupported subsampling ratio"); - } - - break; - } - - break; - } - - this.ComponentArray[i].HorizontalFactor = h; - this.ComponentArray[i].VerticalFactor = v; - } - - int h0 = this.ComponentArray[0].HorizontalFactor; - int v0 = this.ComponentArray[0].VerticalFactor; - this.MCUCountX = (this.ImageWidth + (8 * h0) - 1) / (8 * h0); - this.MCUCountY = (this.ImageHeight + (8 * v0) - 1) / (8 * v0); - - // As a preparation for parallelizing Scan decoder, we also allocate DecodedBlocks in the non-progressive case! - for (int i = 0; i < this.ComponentCount; i++) - { - int count = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor - * this.ComponentArray[i].VerticalFactor; - this.DecodedBlocks[i] = Buffer.CreateClean(count); - } - } - } -} diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 6c6561468..3d79faabc 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Encoder for writing the data image to a stream in jpeg format. /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs index 23cd5d875..4f368dcde 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegFormat.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegFormat.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Registers the image encoders, decoders and mime type detectors for the jpeg format. /// @@ -19,9 +18,9 @@ namespace ImageSharp.Formats public string DefaultMimeType => "image/jpeg"; /// - public IEnumerable MimeTypes => JpegConstants.MimeTypes; + public IEnumerable MimeTypes => OrigJpegConstants.MimeTypes; /// - public IEnumerable FileExtensions => JpegConstants.FileExtensions; + public IEnumerable FileExtensions => OrigJpegConstants.FileExtensions; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs b/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs index b72b290c0..d888986f3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Jpeg +{ /// /// Detects Jpeg file headers /// diff --git a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs index 287323bdd..855815705 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegSubsample.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Jpeg { /// /// Enumerates the chroma subsampling method applied to the image. diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs new file mode 100644 index 000000000..9fba4ae9b --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsAdobe.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Provides information about the Adobe marker segment + /// + internal struct PdfJsAdobe : IEquatable + { + /// + /// The DCT Encode Version + /// + public short DCTEncodeVersion; + + /// + /// 0x0 : (none) + /// Bit 15 : Encoded with Blend=1 downsampling + /// + public short APP14Flags0; + + /// + /// 0x0 : (none) + /// + public short APP14Flags1; + + /// + /// Determines the colorspace transform + /// 00 : Unknown (RGB or CMYK) + /// 01 : YCbCr + /// 02 : YCCK + /// + public byte ColorTransform; + + /// + public bool Equals(PdfJsAdobe other) + { + return this.DCTEncodeVersion == other.DCTEncodeVersion + && this.APP14Flags0 == other.APP14Flags0 + && this.APP14Flags1 == other.APP14Flags1 + && this.ColorTransform == other.ColorTransform; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is PdfJsAdobe && this.Equals((PdfJsAdobe)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + // TODO: Merge and use HashCodeHelpers + int hashCode = this.DCTEncodeVersion.GetHashCode(); + hashCode = (hashCode * 397) ^ this.APP14Flags0.GetHashCode(); + hashCode = (hashCode * 397) ^ this.APP14Flags1.GetHashCode(); + hashCode = (hashCode * 397) ^ this.ColorTransform.GetHashCode(); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs new file mode 100644 index 000000000..3c35e311f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponent.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a component block + /// + internal class PdfJsComponent : IDisposable + { +#pragma warning disable SA1401 + /// + /// Gets or sets the output + /// + public Buffer Output; + + /// + /// Gets or sets the scaling factors + /// + public Vector2 Scale; + + /// + /// Gets or sets the number of blocks per line + /// + public int BlocksPerLine; + + /// + /// Gets or sets the number of blocks per column + /// + public int BlocksPerColumn; + + /// + public void Dispose() + { + this.Output?.Dispose(); + this.Output = null; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs new file mode 100644 index 000000000..86a0c6b31 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsComponentBlocks.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Contains all the decoded component blocks + /// + internal sealed class PdfJsComponentBlocks : IDisposable + { + /// + /// Gets or sets the component blocks + /// + public PdfJsComponent[] Components { get; set; } + + /// + public void Dispose() + { + if (this.Components != null) + { + for (int i = 0; i < this.Components.Length; i++) + { + this.Components[i].Dispose(); + } + + this.Components = null; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs new file mode 100644 index 000000000..d6ff1e9ed --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a jpeg file marker + /// + internal struct PdfJsFileMarker + { + /// + /// Initializes a new instance of the struct. + /// + /// The marker + /// The position within the stream + public PdfJsFileMarker(ushort marker, long position) + { + this.Marker = marker; + this.Position = position; + this.Invalid = false; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The marker + /// The position within the stream + /// Whether the current marker is invalid + public PdfJsFileMarker(ushort marker, long position, bool invalid) + { + this.Marker = marker; + this.Position = position; + this.Invalid = invalid; + } + + /// + /// Gets or sets a value indicating whether the current marker is invalid + /// + public bool Invalid { get; set; } + + /// + /// Gets the position of the marker within a stream + /// + public ushort Marker { get; } + + /// + /// Gets the position of the marker within a stream + /// + public long Position { get; } + + /// + public override string ToString() + { + return this.Marker.ToString("X"); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs new file mode 100644 index 000000000..8ce981a09 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrame.cs @@ -0,0 +1,102 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represent a single jpeg frame + /// + internal sealed class PdfJsFrame : IDisposable + { + /// + /// Gets or sets a value indicating whether the frame uses the extended specification + /// + public bool Extended { get; set; } + + /// + /// Gets or sets a value indicating whether the frame uses the progressive specification + /// + public bool Progressive { get; set; } + + /// + /// Gets or sets the precision + /// + public byte Precision { get; set; } + + /// + /// Gets or sets the number of scanlines within the frame + /// + public short Scanlines { get; set; } + + /// + /// Gets or sets the number of samples per scanline + /// + public short SamplesPerLine { get; set; } + + /// + /// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4 + /// + public byte ComponentCount { get; set; } + + /// + /// Gets or sets the component id collection + /// + public byte[] ComponentIds { get; set; } + + /// + /// Gets or sets the frame component collection + /// + public PdfJsFrameComponent[] Components { get; set; } + + /// + /// Gets or sets the maximum horizontal sampling factor + /// + public int MaxHorizontalFactor { get; set; } + + /// + /// Gets or sets the maximum vertical sampling factor + /// + public int MaxVerticalFactor { get; set; } + + /// + /// Gets or sets the number of MCU's per line + /// + public int McusPerLine { get; set; } + + /// + /// Gets or sets the number of MCU's per column + /// + public int McusPerColumn { get; set; } + + /// + public void Dispose() + { + if (this.Components != null) + { + for (int i = 0; i < this.Components.Length; i++) + { + this.Components[i].Dispose(); + } + + this.Components = null; + } + } + + /// + /// Allocates the frame component blocks + /// + public void InitComponents() + { + this.McusPerLine = (int)MathF.Ceiling(this.SamplesPerLine / 8F / this.MaxHorizontalFactor); + this.McusPerColumn = (int)MathF.Ceiling(this.Scanlines / 8F / this.MaxVerticalFactor); + + for (int i = 0; i < this.ComponentCount; i++) + { + PdfJsFrameComponent component = this.Components[i]; + component.Init(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs new file mode 100644 index 000000000..f60097dc9 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFrameComponent.cs @@ -0,0 +1,132 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a single frame component + /// + internal class PdfJsFrameComponent : IDisposable, IJpegComponent + { +#pragma warning disable SA1401 // Fields should be private + + public PdfJsFrameComponent(PdfJsFrame frame, byte id, int horizontalFactor, int verticalFactor, byte quantizationTableIndex, int index) + { + this.Frame = frame; + this.Id = id; + this.HorizontalSamplingFactor = horizontalFactor; + this.VerticalSamplingFactor = verticalFactor; + this.QuantizationTableIndex = quantizationTableIndex; + this.Index = index; + } + + /// + /// Gets the component Id + /// + public byte Id { get; } + + /// + /// Gets or sets Pred TODO: What does pred stand for? + /// + public int Pred { get; set; } + + /// + /// Gets the horizontal sampling factor. + /// + public int HorizontalSamplingFactor { get; } + + /// + /// Gets the vertical sampling factor. + /// + public int VerticalSamplingFactor { get; } + + Buffer2D IJpegComponent.SpectralBlocks => throw new NotImplementedException(); + + // TODO: Should be derived from PdfJsComponent.Scale + public Size SubSamplingDivisors => throw new NotImplementedException(); + + /// + public int QuantizationTableIndex { get; } + + /// + /// Gets the block data + /// + public Buffer BlockData { get; private set; } + + /// + public int Index { get; } + + public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks); + + public Size SamplingFactors => new Size(this.HorizontalSamplingFactor, this.VerticalSamplingFactor); + + /// + /// Gets the number of blocks per line + /// + public int WidthInBlocks { get; private set; } + + /// + /// Gets the number of blocks per column + /// + public int HeightInBlocks { get; private set; } + + /// + /// Gets or sets the index for the DC Huffman table + /// + public int DCHuffmanTableId { get; set; } + + /// + /// Gets or sets the index for the AC Huffman table + /// + public int ACHuffmanTableId { get; set; } + + internal int BlocksPerLineForMcu { get; private set; } + + internal int BlocksPerColumnForMcu { get; private set; } + + public PdfJsFrame Frame { get; } + + /// + public void Dispose() + { + this.BlockData?.Dispose(); + this.BlockData = null; + } + + public void Init() + { + this.WidthInBlocks = (int)MathF.Ceiling( + MathF.Ceiling(this.Frame.SamplesPerLine / 8F) * this.HorizontalSamplingFactor / this.Frame.MaxHorizontalFactor); + + this.HeightInBlocks = (int)MathF.Ceiling( + MathF.Ceiling(this.Frame.Scanlines / 8F) * this.VerticalSamplingFactor / this.Frame.MaxVerticalFactor); + + this.BlocksPerLineForMcu = this.Frame.McusPerLine * this.HorizontalSamplingFactor; + this.BlocksPerColumnForMcu = this.Frame.McusPerColumn * this.VerticalSamplingFactor; + + int blocksBufferSize = 64 * this.BlocksPerColumnForMcu * (this.BlocksPerLineForMcu + 1); + + // Pooled. Disposed via frame disposal + this.BlockData = Buffer.CreateClean(blocksBufferSize); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetBlockBufferOffset(int row, int col) + { + return 64 * (((this.WidthInBlocks + 1) * row) + col); + } + + public Span GetBlockBuffer(int row, int col) + { + int offset = this.GetBlockBufferOffset(row, col); + return this.BlockData.Span.Slice(offset, 64); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs new file mode 100644 index 000000000..9dc831567 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTable.cs @@ -0,0 +1,209 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a Huffman Table + /// + internal struct PdfJsHuffmanTable : IDisposable + { + private Buffer lookahead; + private Buffer valOffset; + private Buffer maxcode; + private Buffer huffval; + + /// + /// Initializes a new instance of the struct. + /// + /// The code lengths + /// The huffman values + public PdfJsHuffmanTable(byte[] lengths, byte[] values) + { + this.lookahead = Buffer.CreateClean(256); + this.valOffset = Buffer.CreateClean(18); + this.maxcode = Buffer.CreateClean(18); + + using (var huffsize = Buffer.CreateClean(257)) + using (var huffcode = Buffer.CreateClean(257)) + { + GenerateSizeTable(lengths, huffsize); + GenerateCodeTable(huffsize, huffcode); + GenerateDecoderTables(lengths, huffcode, this.valOffset, this.maxcode); + GenerateLookaheadTables(lengths, values, this.lookahead); + } + + this.huffval = Buffer.CreateClean(values.Length); + Buffer.BlockCopy(values, 0, this.huffval.Array, 0, values.Length); + + this.MaxCode = this.maxcode.Array; + this.ValOffset = this.valOffset.Array; + this.HuffVal = this.huffval.Array; + this.Lookahead = this.lookahead.Array; + } + + /// + /// Gets the max code array + /// + public long[] MaxCode + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the value offset array + /// + public short[] ValOffset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the huffman value array + /// + public byte[] HuffVal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + /// Gets the lookahead array + /// + public short[] Lookahead + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + /// + public void Dispose() + { + this.lookahead?.Dispose(); + this.valOffset?.Dispose(); + this.maxcode?.Dispose(); + this.huffval?.Dispose(); + + this.lookahead = null; + this.valOffset = null; + this.maxcode = null; + this.huffval = null; + } + + /// + /// Figure C.1: make table of Huffman code length for each symbol + /// + /// The code lengths + /// The huffman size span + private static void GenerateSizeTable(byte[] lengths, Span huffsize) + { + short index = 0; + for (short l = 1; l <= 16; l++) + { + byte i = lengths[l]; + for (short j = 0; j < i; j++) + { + huffsize[index] = l; + index++; + } + } + + huffsize[index] = 0; + } + + /// + /// Figure C.2: generate the codes themselves + /// + /// The huffman size span + /// The huffman code span + private static void GenerateCodeTable(Span huffsize, Span huffcode) + { + short k = 0; + short si = huffsize[0]; + short code = 0; + for (short i = 0; i < huffsize.Length; i++) + { + while (huffsize[k] == si) + { + huffcode[k] = code; + code++; + k++; + } + + code <<= 1; + si++; + } + } + + /// + /// Figure F.15: generate decoding tables for bit-sequential decoding + /// + /// The code lengths + /// The huffman code span + /// The value offset span + /// The max code span + private static void GenerateDecoderTables(byte[] lengths, Span huffcode, Span valOffset, Span maxcode) + { + short bitcount = 0; + for (int i = 1; i <= 16; i++) + { + if (lengths[i] != 0) + { + // valoffset[l] = huffval[] index of 1st symbol of code length i, + // minus the minimum code of length i + valOffset[i] = (short)(bitcount - huffcode[bitcount]); + bitcount += lengths[i]; + maxcode[i] = huffcode[bitcount - 1]; // maximum code of length i + } + else + { + maxcode[i] = -1; // -1 if no codes of this length + } + } + + valOffset[17] = 0; + maxcode[17] = 0xFFFFFL; + } + + /// + /// Generates lookup tables to speed up decoding + /// + /// The code lengths + /// The huffman value array + /// The lookahead span + private static void GenerateLookaheadTables(byte[] lengths, byte[] huffval, Span lookahead) + { + int x = 0, code = 0; + + for (int i = 0; i < 8; i++) + { + code <<= 1; + + for (int j = 0; j < lengths[i + 1]; j++) + { + // The codeLength is 1+i, so shift code by 8-(1+i) to + // calculate the high bits for every 8-bit sequence + // whose codeLength's high bits matches code. + // The high 8 bits of lutValue are the encoded value. + // The low 8 bits are 1 plus the codeLength. + byte base2 = (byte)(code << (7 - i)); + short lutValue = (short)((short)(huffval[x] << 8) | (short)(2 + i)); + + for (int k = 0; k < 1 << (7 - i); k++) + { + lookahead[base2 | k] = lutValue; + } + + code++; + x++; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs new file mode 100644 index 000000000..5d59809cc --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsHuffmanTables.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Defines a pair of huffman tables + /// + internal sealed class PdfJsHuffmanTables : IDisposable + { + private readonly PdfJsHuffmanTable[] tables = new PdfJsHuffmanTable[4]; + + /// + /// Gets or sets the table at the given index. + /// + /// The index + /// The + public ref PdfJsHuffmanTable this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref this.tables[index]; + } + } + + /// + public void Dispose() + { + for (int i = 0; i < this.tables.Length; i++) + { + this.tables[i].Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs new file mode 100644 index 000000000..49bdc2423 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsIDCT.cs @@ -0,0 +1,512 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Performs the inverse Descrete Cosine Transform on each frame component. + /// + internal static class PdfJsIDCT + { + /// + /// Precomputed values scaled up by 14 bits + /// + public static readonly short[] Aanscales = + { + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, + 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, + 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, + 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, + 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, + 4520, 3552, 2446, 1247 + }; + + private const int DctCos1 = 4017; // cos(pi/16) + private const int DctSin1 = 799; // sin(pi/16) + private const int DctCos3 = 3406; // cos(3*pi/16) + private const int DctSin3 = 2276; // sin(3*pi/16) + private const int DctCos6 = 1567; // cos(6*pi/16) + private const int DctSin6 = 3784; // sin(6*pi/16) + private const int DctSqrt2 = 5793; // sqrt(2) + private const int DctSqrt1D2 = 2896; // sqrt(2) / 2 + +#pragma warning disable SA1310 // Field names must not contain underscore + private const int FIX_1_082392200 = 277; // FIX(1.082392200) + private const int FIX_1_414213562 = 362; // FIX(1.414213562) + private const int FIX_1_847759065 = 473; // FIX(1.847759065) + private const int FIX_2_613125930 = 669; // FIX(2.613125930) +#pragma warning restore SA1310 // Field names must not contain underscore + + private const int ConstBits = 8; + private const int Pass1Bits = 2; // Factional bits in scale factors + private const int MaxJSample = 255; + private const int CenterJSample = 128; + private const int RangeCenter = (MaxJSample * 2) + 2; + + // First segment of range limit table: limit[x] = 0 for x < 0 + // allow negative subscripts of simple table + private const int TableOffset = 2 * (MaxJSample + 1); + private const int LimitOffset = TableOffset - (RangeCenter - CenterJSample); + + // Each IDCT routine is responsible for range-limiting its results and + // converting them to unsigned form (0..MaxJSample). The raw outputs could + // be quite far out of range if the input data is corrupt, so a bulletproof + // range-limiting step is required. We use a mask-and-table-lookup method + // to do the combined operations quickly, assuming that MaxJSample+1 + // is a power of 2. + private const int RangeMask = (MaxJSample * 4) + 3; // 2 bits wider than legal samples + + private static readonly byte[] Limit = new byte[5 * (MaxJSample + 1)]; + + static PdfJsIDCT() + { + // Main part of range limit table: limit[x] = x + int i; + for (i = 0; i <= MaxJSample; i++) + { + Limit[TableOffset + i] = (byte)i; + } + + // End of range limit table: Limit[x] = MaxJSample for x > MaxJSample + for (; i < 3 * (MaxJSample + 1); i++) + { + Limit[TableOffset + i] = MaxJSample; + } + } + + /// + /// A port of Poppler's IDCT method which in turn is taken from: + /// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, + /// 'Practical Fast 1-D DCT Algorithms with 11 Multiplications', + /// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991. + /// + /// The fram component + /// The block buffer offset + /// The computational buffer for holding temp values + /// The quantization table + public static void QuantizeAndInverse(PdfJsFrameComponent component, int blockBufferOffset, ref Span computationBuffer, ref Span quantizationTable) + { + Span blockData = component.BlockData.Slice(blockBufferOffset); + int v0, v1, v2, v3, v4, v5, v6, v7; + int p0, p1, p2, p3, p4, p5, p6, p7; + int t; + + // inverse DCT on rows + for (int row = 0; row < 64; row += 8) + { + // gather block data + p0 = blockData[row]; + p1 = blockData[row + 1]; + p2 = blockData[row + 2]; + p3 = blockData[row + 3]; + p4 = blockData[row + 4]; + p5 = blockData[row + 5]; + p6 = blockData[row + 6]; + p7 = blockData[row + 7]; + + // dequant p0 + p0 *= quantizationTable[row]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + t = ((DctSqrt2 * p0) + 512) >> 10; + short st = (short)t; + computationBuffer[row] = st; + computationBuffer[row + 1] = st; + computationBuffer[row + 2] = st; + computationBuffer[row + 3] = st; + computationBuffer[row + 4] = st; + computationBuffer[row + 5] = st; + computationBuffer[row + 6] = st; + computationBuffer[row + 7] = st; + continue; + } + + // dequant p1 ... p7 + p1 *= quantizationTable[row + 1]; + p2 *= quantizationTable[row + 2]; + p3 *= quantizationTable[row + 3]; + p4 *= quantizationTable[row + 4]; + p5 *= quantizationTable[row + 5]; + p6 *= quantizationTable[row + 6]; + p7 *= quantizationTable[row + 7]; + + // stage 4 + v0 = ((DctSqrt2 * p0) + 128) >> 8; + v1 = ((DctSqrt2 * p4) + 128) >> 8; + v2 = p2; + v3 = p6; + v4 = ((DctSqrt1D2 * (p1 - p7)) + 128) >> 8; + v7 = ((DctSqrt1D2 * (p1 + p7)) + 128) >> 8; + v5 = p3 << 4; + v6 = p5 << 4; + + // stage 3 + v0 = (v0 + v1 + 1) >> 1; + v1 = v0 - v1; + t = ((v2 * DctSin6) + (v3 * DctCos6) + 128) >> 8; + v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 128) >> 8; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; + v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; + v7 = t; + t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; + v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; + v6 = t; + + // stage 1 + computationBuffer[row] = (short)(v0 + v7); + computationBuffer[row + 7] = (short)(v0 - v7); + computationBuffer[row + 1] = (short)(v1 + v6); + computationBuffer[row + 6] = (short)(v1 - v6); + computationBuffer[row + 2] = (short)(v2 + v5); + computationBuffer[row + 5] = (short)(v2 - v5); + computationBuffer[row + 3] = (short)(v3 + v4); + computationBuffer[row + 4] = (short)(v3 - v4); + } + + // inverse DCT on columns + for (int col = 0; col < 8; ++col) + { + p0 = computationBuffer[col]; + p1 = computationBuffer[col + 8]; + p2 = computationBuffer[col + 16]; + p3 = computationBuffer[col + 24]; + p4 = computationBuffer[col + 32]; + p5 = computationBuffer[col + 40]; + p6 = computationBuffer[col + 48]; + p7 = computationBuffer[col + 56]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + t = ((DctSqrt2 * p0) + 8192) >> 14; + + // convert to 8 bit + t = (t < -2040) ? 0 : (t >= 2024) ? MaxJSample : (t + 2056) >> 4; + short st = (short)t; + + blockData[col] = st; + blockData[col + 8] = st; + blockData[col + 16] = st; + blockData[col + 24] = st; + blockData[col + 32] = st; + blockData[col + 40] = st; + blockData[col + 48] = st; + blockData[col + 56] = st; + continue; + } + + // stage 4 + v0 = ((DctSqrt2 * p0) + 2048) >> 12; + v1 = ((DctSqrt2 * p4) + 2048) >> 12; + v2 = p2; + v3 = p6; + v4 = ((DctSqrt1D2 * (p1 - p7)) + 2048) >> 12; + v7 = ((DctSqrt1D2 * (p1 + p7)) + 2048) >> 12; + v5 = p3; + v6 = p5; + + // stage 3 + // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when + // converting to UInt8 range later. + v0 = ((v0 + v1 + 1) >> 1) + 4112; + v1 = v0 - v1; + t = ((v2 * DctSin6) + (v3 * DctCos6) + 2048) >> 12; + v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 2048) >> 12; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; + v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; + v7 = t; + t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; + v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; + v6 = t; + + // stage 1 + p0 = v0 + v7; + p7 = v0 - v7; + p1 = v1 + v6; + p6 = v1 - v6; + p2 = v2 + v5; + p5 = v2 - v5; + p3 = v3 + v4; + p4 = v3 - v4; + + // convert to 8-bit integers + p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? MaxJSample : p0 >> 4; + p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? MaxJSample : p1 >> 4; + p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? MaxJSample : p2 >> 4; + p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? MaxJSample : p3 >> 4; + p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? MaxJSample : p4 >> 4; + p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? MaxJSample : p5 >> 4; + p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? MaxJSample : p6 >> 4; + p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? MaxJSample : p7 >> 4; + + // store block data + blockData[col] = (short)p0; + blockData[col + 8] = (short)p1; + blockData[col + 16] = (short)p2; + blockData[col + 24] = (short)p3; + blockData[col + 32] = (short)p4; + blockData[col + 40] = (short)p5; + blockData[col + 48] = (short)p6; + blockData[col + 56] = (short)p7; + } + } + + /// + /// A port of + /// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + /// on each row(or vice versa, but it's more convenient to emit a row at + /// a time). Direct algorithms are also available, but they are much more + /// complex and seem not to be any faster when reduced to code. + /// + /// This implementation is based on Arai, Agui, and Nakajima's algorithm for + /// scaled DCT.Their original paper (Trans.IEICE E-71(11):1095) is in + /// Japanese, but the algorithm is described in the Pennebaker & Mitchell + /// JPEG textbook(see REFERENCES section in file README.ijg). The following + /// code is based directly on figure 4-8 in P&M. + /// While an 8-point DCT cannot be done in less than 11 multiplies, it is + /// possible to arrange the computation so that many of the multiplies are + /// simple scalings of the final outputs.These multiplies can then be + /// folded into the multiplications or divisions by the JPEG quantization + /// table entries. The AA&N method leaves only 5 multiplies and 29 adds + /// to be done in the DCT itself. + /// The primary disadvantage of this method is that with fixed-point math, + /// accuracy is lost due to imprecise representation of the scaled + /// quantization values.The smaller the quantization table entry, the less + /// precise the scaled value, so this implementation does worse with high - + /// quality - setting files than with low - quality ones. + /// + /// The frame component + /// The block buffer offset + /// The computational buffer for holding temp values + /// The multiplier table + public static void QuantizeAndInverseFast(PdfJsFrameComponent component, int blockBufferOffset, ref Span computationBuffer, ref Span multiplierTable) + { + Span blockData = component.BlockData.Slice(blockBufferOffset); + int p0, p1, p2, p3, p4, p5, p6, p7; + + for (int col = 0; col < 8; col++) + { + // Gather block data + p0 = blockData[col]; + p1 = blockData[col + 8]; + p2 = blockData[col + 16]; + p3 = blockData[col + 24]; + p4 = blockData[col + 32]; + p5 = blockData[col + 40]; + p6 = blockData[col + 48]; + p7 = blockData[col + 56]; + + int tmp0 = p0 * multiplierTable[col]; + + // Due to quantization, we will usually find that many of the input + // coefficients are zero, especially the AC terms. We can exploit this + // by short-circuiting the IDCT calculation for any column in which all + // the AC terms are zero. In that case each output is equal to the + // DC coefficient (with scale factor as needed). + // With typical images and quantization tables, half or more of the + // column DCT calculations can be simplified this way. + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + short dcval = (short)tmp0; + + computationBuffer[col] = dcval; + computationBuffer[col + 8] = dcval; + computationBuffer[col + 16] = dcval; + computationBuffer[col + 24] = dcval; + computationBuffer[col + 32] = dcval; + computationBuffer[col + 40] = dcval; + computationBuffer[col + 48] = dcval; + computationBuffer[col + 56] = dcval; + + continue; + } + + // Even part + int tmp1 = p2 * multiplierTable[col + 16]; + int tmp2 = p4 * multiplierTable[col + 32]; + int tmp3 = p6 * multiplierTable[col + 48]; + + int tmp10 = tmp0 + tmp2; // Phase 3 + int tmp11 = tmp0 - tmp2; + + int tmp13 = tmp1 + tmp3; // Phases 5-3 + int tmp12 = Multiply(tmp1 - tmp3, FIX_1_414213562) - tmp13; // 2*c4 + + tmp0 = tmp10 + tmp13; // Phase 2 + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + // Odd Part + int tmp4 = p1 * multiplierTable[col + 8]; + int tmp5 = p3 * multiplierTable[col + 24]; + int tmp6 = p5 * multiplierTable[col + 40]; + int tmp7 = p7 * multiplierTable[col + 56]; + + int z13 = tmp6 + tmp5; // Phase 6 + int z10 = tmp6 - tmp5; + int z11 = tmp4 + tmp7; + int z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; // Phase 5 + tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4 + + int z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2 + tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6) + tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6) + + tmp6 = tmp12 - tmp7; // Phase 2 + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 - tmp5; + + computationBuffer[col] = (short)(tmp0 + tmp7); + computationBuffer[col + 56] = (short)(tmp0 - tmp7); + computationBuffer[col + 8] = (short)(tmp1 + tmp6); + computationBuffer[col + 48] = (short)(tmp1 - tmp6); + computationBuffer[col + 16] = (short)(tmp2 + tmp5); + computationBuffer[col + 40] = (short)(tmp2 - tmp5); + computationBuffer[col + 24] = (short)(tmp3 + tmp4); + computationBuffer[col + 32] = (short)(tmp3 - tmp4); + } + + // Pass 2: process rows from work array, store into output array. + // Note that we must descale the results by a factor of 8 == 2**3, + // and also undo the pass 1 bits scaling. + for (int row = 0; row < 64; row += 8) + { + p1 = computationBuffer[row + 1]; + p2 = computationBuffer[row + 2]; + p3 = computationBuffer[row + 3]; + p4 = computationBuffer[row + 4]; + p5 = computationBuffer[row + 5]; + p6 = computationBuffer[row + 6]; + p7 = computationBuffer[row + 7]; + + // Add range center and fudge factor for final descale and range-limit. + int z5 = computationBuffer[row] + (RangeCenter << (Pass1Bits + 3)) + (1 << (Pass1Bits + 2)); + + // Check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) + { + byte dcval = Limit[LimitOffset + (RightShift(z5, Pass1Bits + 3) & RangeMask)]; + + blockData[row] = dcval; + blockData[row + 1] = dcval; + blockData[row + 2] = dcval; + blockData[row + 3] = dcval; + blockData[row + 4] = dcval; + blockData[row + 5] = dcval; + blockData[row + 6] = dcval; + blockData[row + 7] = dcval; + + continue; + } + + // Even part + int tmp10 = z5 + p4; + int tmp11 = z5 - p4; + + int tmp13 = p2 + p6; + int tmp12 = Multiply(p2 - p6, FIX_1_414213562) - tmp13; // 2*c4 + + int tmp0 = tmp10 + tmp13; + int tmp3 = tmp10 - tmp13; + int tmp1 = tmp11 + tmp12; + int tmp2 = tmp11 - tmp12; + + // Odd part + int z13 = p5 + p3; + int z10 = p5 - p3; + int z11 = p1 + p7; + int z12 = p1 - p7; + + int tmp7 = z11 + z13; // Phase 5 + tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4 + + z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2 + tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6) + tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6) + + int tmp6 = tmp12 - tmp7; // Phase 2 + int tmp5 = tmp11 - tmp6; + int tmp4 = tmp10 - tmp5; + + // Final output stage: scale down by a factor of 8, offset, and range-limit + blockData[row] = Limit[LimitOffset + (RightShift(tmp0 + tmp7, Pass1Bits + 3) & RangeMask)]; + blockData[row + 7] = Limit[LimitOffset + (RightShift(tmp0 - tmp7, Pass1Bits + 3) & RangeMask)]; + blockData[row + 1] = Limit[LimitOffset + (RightShift(tmp1 + tmp6, Pass1Bits + 3) & RangeMask)]; + blockData[row + 6] = Limit[LimitOffset + (RightShift(tmp1 - tmp6, Pass1Bits + 3) & RangeMask)]; + blockData[row + 2] = Limit[LimitOffset + (RightShift(tmp2 + tmp5, Pass1Bits + 3) & RangeMask)]; + blockData[row + 5] = Limit[LimitOffset + (RightShift(tmp2 - tmp5, Pass1Bits + 3) & RangeMask)]; + blockData[row + 3] = Limit[LimitOffset + (RightShift(tmp3 + tmp4, Pass1Bits + 3) & RangeMask)]; + blockData[row + 4] = Limit[LimitOffset + (RightShift(tmp3 - tmp4, Pass1Bits + 3) & RangeMask)]; + } + } + + /// + /// Descale and correctly round an int value that's scaled by bits. + /// We assume rounds towards minus infinity, so adding + /// the fudge factor is correct for either sign of . + /// + /// The value + /// The number of bits + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Descale(int value, int n) + { + return RightShift(value + (1 << (n - 1)), n); + } + + /// + /// Multiply a variable by an int constant, and immediately descale. + /// + /// The value + /// The multiplier + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Multiply(int val, int c) + { + return Descale(val * c, ConstBits); + } + + /// + /// Right-shifts the value by the given amount + /// + /// The value + /// The amount to shift by + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RightShift(int value, int shift) + { + return value >> shift; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs new file mode 100644 index 000000000..52ba81bbc --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJFif.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Provides information about the JFIF marker segment + /// TODO: Thumbnail? + /// + internal struct PdfJsJFif : IEquatable + { + /// + /// The major version + /// + public byte MajorVersion; + + /// + /// The minor version + /// + public byte MinorVersion; + + /// + /// Units for the following pixel density fields + /// 00 : No units; width:height pixel aspect ratio = Ydensity:Xdensity + /// 01 : Pixels per inch (2.54 cm) + /// 02 : Pixels per centimeter + /// + public byte DensityUnits; + + /// + /// Horizontal pixel density. Must not be zero. + /// + public short XDensity; + + /// + /// Vertical pixel density. Must not be zero. + /// + public short YDensity; + + /// + public bool Equals(PdfJsJFif other) + { + return this.MajorVersion == other.MajorVersion + && this.MinorVersion == other.MinorVersion + && this.DensityUnits == other.DensityUnits + && this.XDensity == other.XDensity + && this.YDensity == other.YDensity; + } + + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is PdfJsJFif && this.Equals((PdfJsJFif)obj); + } + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = this.MajorVersion.GetHashCode(); + hashCode = (hashCode * 397) ^ this.MinorVersion.GetHashCode(); + hashCode = (hashCode * 397) ^ this.DensityUnits.GetHashCode(); + hashCode = (hashCode * 397) ^ this.XDensity.GetHashCode(); + hashCode = (hashCode * 397) ^ this.YDensity.GetHashCode(); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs new file mode 100644 index 000000000..034986c2c --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsJpegPixelArea.cs @@ -0,0 +1,145 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Represents a section of the jpeg component data laid out in pixel order. + /// + internal struct PdfJsJpegPixelArea : IDisposable + { + private readonly int imageWidth; + + private readonly int imageHeight; + + private Buffer componentData; + + private int rowStride; + + /// + /// Initializes a new instance of the struct. + /// + /// The image width + /// The image height + /// The number of components + public PdfJsJpegPixelArea(int imageWidth, int imageHeight, int numberOfComponents) + { + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.Width = 0; + this.Height = 0; + this.NumberOfComponents = numberOfComponents; + this.componentData = null; + this.rowStride = 0; + } + + /// + /// Gets the number of components + /// + public int NumberOfComponents { get; } + + /// + /// Gets the width + /// + public int Width { get; private set; } + + /// + /// Gets the height + /// + public int Height { get; private set; } + + /// + /// Organsizes the decoded jpeg components into a linear array ordered by component. + /// This must be called before attempting to retrieve the data. + /// + /// The jpeg component blocks + /// The pixel area width + /// The pixel area height + public void LinearizeBlockData(PdfJsComponentBlocks components, int width, int height) + { + this.Width = width; + this.Height = height; + int numberOfComponents = this.NumberOfComponents; + this.rowStride = width * numberOfComponents; + var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); + + this.componentData = new Buffer(width * height * numberOfComponents); + Span componentDataSpan = this.componentData; + const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs + + using (var xScaleBlockOffset = new Buffer(width)) + { + Span xScaleBlockOffsetSpan = xScaleBlockOffset; + for (int i = 0; i < numberOfComponents; i++) + { + ref PdfJsComponent component = ref components.Components[i]; + Vector2 componentScale = component.Scale * scale; + int offset = i; + Span output = component.Output; + int blocksPerScanline = (component.BlocksPerLine + 1) << 3; + + // Precalculate the xScaleBlockOffset + int j; + for (int x = 0; x < width; x++) + { + j = (int)(x * componentScale.X); + xScaleBlockOffsetSpan[x] = (int)((j & Mask3Lsb) << 3) | (j & 7); + } + + // Linearize the blocks of the component + for (int y = 0; y < height; y++) + { + j = (int)(y * componentScale.Y); + int index = blocksPerScanline * (int)(j & Mask3Lsb) | ((j & 7) << 3); + for (int x = 0; x < width; x++) + { + componentDataSpan[offset] = (byte)output[index + xScaleBlockOffsetSpan[x]]; + offset += numberOfComponents; + } + } + } + } + } + + /// + /// Gets a representing the row 'y' beginning from the the first byte on that row. + /// + /// The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the pixel area. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int y) + { + this.CheckCoordinates(y); + return this.componentData.Slice(y * this.rowStride, this.rowStride); + } + + /// + public void Dispose() + { + this.componentData?.Dispose(); + this.componentData = null; + } + + /// + /// Checks the coordinates to ensure they are within bounds. + /// + /// The y-coordinate of the row. Must be greater than zero and less than the height of the area. + /// + /// Thrown if the coordinates are not within the bounds of the image. + /// + [Conditional("DEBUG")] + private void CheckCoordinates(int y) + { + if (y < 0 || y >= this.Height) + { + throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the area bounds."); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs new file mode 100644 index 000000000..1000ce82c --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsQuantizationTables.cs @@ -0,0 +1,64 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Contains the quantization tables. + /// + internal sealed class PdfJsQuantizationTables : IDisposable + { + /// + /// Gets the ZigZag scan table + /// + public static byte[] DctZigZag + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + } + + = + { + 0, + 1, 8, + 16, 9, 2, + 3, 10, 17, 24, + 32, 25, 18, 11, 4, + 5, 12, 19, 26, 33, 40, + 48, 41, 34, 27, 20, 13, 6, + 7, 14, 21, 28, 35, 42, 49, 56, + 57, 50, 43, 36, 29, 22, 15, + 23, 30, 37, 44, 51, 58, + 59, 52, 45, 38, 31, + 39, 46, 53, 60, + 61, 54, 47, + 55, 62, + 63 + }; + + /// + /// Gets or sets the quantization tables. + /// + public Buffer2D Tables + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; set; + } + + = new Buffer2D(64, 4); + + /// + public void Dispose() + { + if (this.Tables != null) + { + this.Tables.Dispose(); + this.Tables = null; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs new file mode 100644 index 000000000..e2e5d985e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsScanDecoder.cs @@ -0,0 +1,937 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ + /// + /// Provides the means to decode a spectral scan + /// + internal struct PdfJsScanDecoder + { + private byte[] markerBuffer; + + private int bitsData; + + private int bitsCount; + +#pragma warning disable 414 + private int bitsUnRead; + + private int accumulator; +#pragma warning restore 414 + + private int specStart; + + private int specEnd; + + private int eobrun; + + private int compIndex; + + private int successiveState; + + private int successiveACState; + + private int successiveACNextValue; + + private bool endOfStreamReached; + + private bool unexpectedMarkerReached; + + /// + /// Decodes the spectral scan + /// + /// The image frame + /// The input stream + /// The DC Huffman tables + /// The AC Huffman tables + /// The scan components + /// The component index within the array + /// The length of the components. Different to the array length + /// The reset interval + /// The spectral selection start + /// The spectral selection end + /// The successive approximation bit high end + /// The successive approximation bit low end + public void DecodeScan( + PdfJsFrame frame, + Stream stream, + PdfJsHuffmanTables dcHuffmanTables, + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentIndex, + int componentsLength, + ushort resetInterval, + int spectralStart, + int spectralEnd, + int successivePrev, + int successive) + { + this.markerBuffer = new byte[2]; + this.compIndex = componentIndex; + this.specStart = spectralStart; + this.specEnd = spectralEnd; + this.successiveState = successive; + this.endOfStreamReached = false; + this.unexpectedMarkerReached = false; + + bool progressive = frame.Progressive; + int mcusPerLine = frame.McusPerLine; + + int mcu = 0; + int mcuExpected; + if (componentsLength == 1) + { + mcuExpected = components[this.compIndex].WidthInBlocks * components[this.compIndex].HeightInBlocks; + } + else + { + mcuExpected = mcusPerLine * frame.McusPerColumn; + } + + PdfJsFileMarker fileMarker; + while (mcu < mcuExpected) + { + // Reset interval stuff + int mcuToRead = resetInterval != 0 ? Math.Min(mcuExpected - mcu, resetInterval) : mcuExpected; + for (int i = 0; i < components.Length; i++) + { + PdfJsFrameComponent c = components[i]; + c.Pred = 0; + } + + this.eobrun = 0; + + if (!progressive) + { + this.DecodeScanBaseline(dcHuffmanTables, acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + else + { + if (this.specStart == 0) + { + if (successivePrev == 0) + { + this.DecodeScanDCFirst(dcHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + else + { + this.DecodeScanDCSuccessive(components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + } + else + { + if (successivePrev == 0) + { + this.DecodeScanACFirst(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + else + { + this.DecodeScanACSuccessive(acHuffmanTables, components, componentsLength, mcusPerLine, mcuToRead, ref mcu, stream); + } + } + } + + // Find marker + this.bitsCount = 0; + this.accumulator = 0; + this.bitsUnRead = 0; + fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); + + // Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past + // those to attempt to find a valid marker (fixes issue4090.pdf) in original code. + if (fileMarker.Invalid) + { +#if DEBUG + Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); +#endif + } + + ushort marker = fileMarker.Marker; + + // RSTn - We've alread read the bytes and altered the position so no need to skip + if (marker >= PdfJsJpegConstants.Markers.RST0 && marker <= PdfJsJpegConstants.Markers.RST7) + { + continue; + } + + if (!fileMarker.Invalid) + { + // We've found a valid marker. + // Rewind the stream to the position of the marker and break + stream.Position = fileMarker.Position; + break; + } + } + + fileMarker = PdfJsJpegDecoderCore.FindNextFileMarker(this.markerBuffer, stream); + + // Some images include more Scan blocks than expected, skip past those and + // attempt to find the next valid marker (fixes issue8182.pdf) in original code. + if (fileMarker.Invalid) + { +#if DEBUG + Debug.WriteLine($"DecodeScan - Unexpected MCU data at {stream.Position}, next marker is: {fileMarker.Marker:X}"); +#endif + } + else + { + // We've found a valid marker. + // Rewind the stream to the position of the marker + stream.Position = fileMarker.Position; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanBaseline( + PdfJsHuffmanTables dcHuffmanTables, + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuBaseline(ref dcHuffmanTable, ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanDCFirst( + PdfJsHuffmanTables dcHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockDCFirst(ref dcHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable dcHuffmanTable = ref dcHuffmanTables[component.DCHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuDCFirst(ref dcHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanDCSuccessive( + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockDCSuccessive(component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuDCSuccessive(component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanACFirst( + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockACFirst(ref acHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuACFirst(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeScanACSuccessive( + PdfJsHuffmanTables acHuffmanTables, + PdfJsFrameComponent[] components, + int componentsLength, + int mcusPerLine, + int mcuToRead, + ref int mcu, + Stream stream) + { + if (componentsLength == 1) + { + PdfJsFrameComponent component = components[this.compIndex]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + + for (int n = 0; n < mcuToRead; n++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeBlockACSuccessive(ref acHuffmanTable, component, mcu, stream); + mcu++; + } + } + else + { + for (int n = 0; n < mcuToRead; n++) + { + for (int i = 0; i < componentsLength; i++) + { + PdfJsFrameComponent component = components[i]; + ref PdfJsHuffmanTable acHuffmanTable = ref acHuffmanTables[component.ACHuffmanTableId]; + int h = component.HorizontalSamplingFactor; + int v = component.VerticalSamplingFactor; + + for (int j = 0; j < v; j++) + { + for (int k = 0; k < h; k++) + { + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + continue; + } + + this.DecodeMcuACSuccessive(ref acHuffmanTable, component, mcusPerLine, mcu, j, k, stream); + } + } + } + + mcu++; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuBaseline(ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeBaseline(component, offset, ref dcHuffmanTable, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuDCFirst(ref PdfJsHuffmanTable dcHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCFirst(component, offset, ref dcHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockDCSuccessive(PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCSuccessive(component, offset, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuDCSuccessive(PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeDCSuccessive(component, offset, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACFirst(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuACFirst(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACFirst(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBlockACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcu, Stream stream) + { + int blockRow = mcu / component.WidthInBlocks; + int blockCol = mcu % component.WidthInBlocks; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeMcuACSuccessive(ref PdfJsHuffmanTable acHuffmanTable, PdfJsFrameComponent component, int mcusPerLine, int mcu, int row, int col, Stream stream) + { + int mcuRow = mcu / mcusPerLine; + int mcuCol = mcu % mcusPerLine; + int blockRow = (mcuRow * component.VerticalSamplingFactor) + row; + int blockCol = (mcuCol * component.HorizontalSamplingFactor) + col; + int offset = component.GetBlockBufferOffset(blockRow, blockCol); + this.DecodeACSuccessive(component, offset, ref acHuffmanTable, stream); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ReadBit(Stream stream) + { + // TODO: I wonder if we can do this two bytes at a time; libjpeg turbo seems to do that? + if (this.bitsCount > 0) + { + this.bitsCount--; + return (this.bitsData >> this.bitsCount) & 1; + } + + this.bitsData = stream.ReadByte(); + + if (this.bitsData == -0x1) + { + // We've encountered the end of the file stream which means there's no EOI marker in the image + this.endOfStreamReached = true; + } + + if (this.bitsData == PdfJsJpegConstants.Markers.Prefix) + { + int nextByte = stream.ReadByte(); + if (nextByte != 0) + { +#if DEBUG + Debug.WriteLine($"DecodeScan - Unexpected marker {(this.bitsData << 8) | nextByte:X} at {stream.Position}"); +#endif + + // We've encountered an unexpected marker. Reverse the stream and exit. + this.unexpectedMarkerReached = true; + stream.Position -= 2; + } + + // Unstuff 0 + } + + this.bitsCount = 7; + + return this.bitsData >> 7; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private short DecodeHuffman(ref PdfJsHuffmanTable tree, Stream stream) + { + short code = -1; + + // TODO: Adding this code introduces error into the decoder. + // NOTES # During investigation of the libjpeg implementation it appears that they pull 32bits at a time and operate on those bits + // using 3 methods: FillBits, PeekBits, and ReadBits. We should attempt to do the same. + // It doesn't appear to speed anything up either. + // if (this.bitsUnRead < 8) + // { + // if (this.bitsCount <= 0) + // { + // code = (short)this.ReadBit(stream); + // if (this.endOfStreamReached || this.unexpectedMarkerReached) + // { + // return -1; + // } + // + // this.bitsUnRead += 8; + // } + // + // this.accumulator = (this.accumulator << 8) | this.bitsData; + // int lutIndex = (this.accumulator >> (8 - this.bitsUnRead)) & 0xFF; + // int v = tree.Lookahead[lutIndex]; + // if (v != 0) + // { + // int nb = (v & 0xFF) - 1; + // this.bitsCount -= nb - 1; + // this.bitsUnRead -= nb; + // v = v >> 8; + // return (short)v; + // } + // } + if (code == -1) + { + code = (short)this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return -1; + } + } + + // "DECODE", section F.2.2.3, figure F.16, page 109 of T.81 + int i = 1; + + while (code > tree.MaxCode[i]) + { + code <<= 1; + code |= (short)this.ReadBit(stream); + + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return -1; + } + + i++; + } + + int j = tree.ValOffset[i]; + return tree.HuffVal[(j + code) & 0xFF]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int Receive(int length, Stream stream) + { + int n = 0; + while (length > 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return -1; + } + + n = (n << 1) | bit; + length--; + } + + return n; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ReceiveAndExtend(int length, Stream stream) + { + if (length == 1) + { + return this.ReadBit(stream) == 1 ? 1 : -1; + } + + int n = this.Receive(length, stream); + if (n >= 1 << (length - 1)) + { + return n; + } + + return n + (-1 << length) + 1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeBaseline(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + { + int t = this.DecodeHuffman(ref dcHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream); + component.BlockData[offset] = (short)(component.Pred += diff); + + int k = 1; + while (k < 64) + { + int rs = this.DecodeHuffman(ref acHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int s = rs & 15; + int r = rs >> 4; + + if (s == 0) + { + if (r < 15) + { + break; + } + + k += 16; + continue; + } + + k += r; + + if (k > 63) + { + break; + } + + byte z = PdfJsQuantizationTables.DctZigZag[k]; + short re = (short)this.ReceiveAndExtend(s, stream); + component.BlockData[offset + z] = re; + k++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeDCFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable dcHuffmanTable, Stream stream) + { + int t = this.DecodeHuffman(ref dcHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int diff = t == 0 ? 0 : this.ReceiveAndExtend(t, stream) << this.successiveState; + component.BlockData[offset] = (short)(component.Pred += diff); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeDCSuccessive(PdfJsFrameComponent component, int offset, Stream stream) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + component.BlockData[offset] |= (short)(bit << this.successiveState); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeACFirst(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + { + if (this.eobrun > 0) + { + this.eobrun--; + return; + } + + Span componentBlockDataSpan = component.BlockData.Span; + int k = this.specStart; + int e = this.specEnd; + while (k <= e) + { + short rs = this.DecodeHuffman(ref acHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int s = rs & 15; + int r = rs >> 4; + + if (s == 0) + { + if (r < 15) + { + this.eobrun = this.Receive(r, stream) + (1 << r) - 1; + break; + } + + k += 16; + continue; + } + + k += r; + byte z = PdfJsQuantizationTables.DctZigZag[k]; + componentBlockDataSpan[offset + z] = (short)(this.ReceiveAndExtend(s, stream) * (1 << this.successiveState)); + k++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecodeACSuccessive(PdfJsFrameComponent component, int offset, ref PdfJsHuffmanTable acHuffmanTable, Stream stream) + { + int k = this.specStart; + int e = this.specEnd; + int r = 0; + Span componentBlockDataSpan = component.BlockData.Span; + while (k <= e) + { + byte z = PdfJsQuantizationTables.DctZigZag[k]; + switch (this.successiveACState) + { + case 0: // Initial state + short rs = this.DecodeHuffman(ref acHuffmanTable, stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + int s = rs & 15; + r = rs >> 4; + if (s == 0) + { + if (r < 15) + { + this.eobrun = this.Receive(r, stream) + (1 << r); + this.successiveACState = 4; + } + else + { + r = 16; + this.successiveACState = 1; + } + } + else + { + if (s != 1) + { + throw new ImageFormatException("Invalid ACn encoding"); + } + + this.successiveACNextValue = this.ReceiveAndExtend(s, stream); + this.successiveACState = r > 0 ? 2 : 3; + } + + continue; + case 1: // Skipping r zero items + case 2: + if (componentBlockDataSpan[offset + z] != 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + } + else + { + r--; + if (r == 0) + { + this.successiveACState = this.successiveACState == 2 ? 3 : 0; + } + } + + break; + case 3: // Set value for a zero item + if (componentBlockDataSpan[offset + z] != 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + } + else + { + componentBlockDataSpan[offset + z] = (short)(this.successiveACNextValue << this.successiveState); + this.successiveACState = 0; + } + + break; + case 4: // Eob + if (componentBlockDataSpan[offset + z] != 0) + { + int bit = this.ReadBit(stream); + if (this.endOfStreamReached || this.unexpectedMarkerReached) + { + return; + } + + componentBlockDataSpan[offset + z] += (short)(bit << this.successiveState); + } + + break; + } + + k++; + } + + if (this.successiveACState == 4) + { + this.eobrun--; + if (this.eobrun == 0) + { + this.successiveACState = 0; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs similarity index 51% rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs rename to src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs index 5c9e8f9fc..ddc577270 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsYCbCrToRgbTables.cs @@ -1,71 +1,70 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components +{ /// /// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace. /// Methods to build the tables are based on libjpeg implementation. /// - internal unsafe struct YCbCrToRgbTables + internal struct PdfJsYCbCrToRgbTables { /// /// The red red-chrominance table /// - public fixed int CrRTable[256]; + public static int[] CrRTable = new int[256]; /// /// The blue blue-chrominance table /// - public fixed int CbBTable[256]; + public static int[] CbBTable = new int[256]; /// /// The green red-chrominance table /// - public fixed int CrGTable[256]; + public static int[] CrGTable = new int[256]; /// /// The green blue-chrominance table /// - public fixed int CbGTable[256]; + public static int[] CbGTable = new int[256]; // Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places. private const int ScaleBits = 16; private const int Half = 1 << (ScaleBits - 1); + private const int MinSample = 0; + + private const int HalfSample = 128; + + private const int MaxSample = 255; + /// /// Initializes the YCbCr tables /// - /// The intialized - public static YCbCrToRgbTables Create() + public static void Create() { - YCbCrToRgbTables tables = default(YCbCrToRgbTables); - for (int i = 0, x = -128; i <= 255; i++, x++) { // i is the actual input pixel value, in the range 0..255 // The Cb or Cr value we are thinking of is x = i - 128 // Cr=>R value is nearest int to 1.402 * x - tables.CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); + CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); // Cb=>B value is nearest int to 1.772 * x - tables.CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); + CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); // Cr=>G value is scaled-up -0.714136286 - tables.CrGTable[i] = (-Fix(0.714136286F)) * x; + CrGTable[i] = (-Fix(0.714136286F)) * x; // Cb => G value is scaled - up - 0.344136286 * x // We also add in Half so that need not do it in inner loop - tables.CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; + CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; } - - return tables; } /// @@ -73,27 +72,50 @@ namespace ImageSharp.Formats.Jpg /// /// The pixel format. /// The packed pixel. - /// The reference to the tables instance. /// The y luminance component. /// The cb chroma component. /// The cr chroma component. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Pack(ref TPixel packed, YCbCrToRgbTables* tables, byte y, byte cb, byte cr) + public static void PackYCbCr(ref TPixel packed, byte y, byte cb, byte cr) where TPixel : struct, IPixel { - // float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); - byte r = (byte)(y + tables->CrRTable[cr]).Clamp(0, 255); + byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255); - // float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); // The values for the G calculation are left scaled up, since we must add them together before rounding. - byte g = (byte)(y + RightShift(tables->CbGTable[cb] + tables->CrGTable[cr])).Clamp(0, 255); + byte g = (byte)(y + RightShift(CbGTable[cb] + CrGTable[cr])).Clamp(0, 255); - // float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); - byte b = (byte)(y + tables->CbBTable[cb]).Clamp(0, 255); + byte b = (byte)(y + CbBTable[cb]).Clamp(0, 255); packed.PackFromRgba32(new Rgba32(r, g, b, 255)); } + /// + /// Optimized method to pack bytes to the image from the YccK color space. + /// + /// The pixel format. + /// The packed pixel. + /// The y luminance component. + /// The cb chroma component. + /// The cr chroma component. + /// The keyline component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void PackYccK(ref TPixel packed, byte y, byte cb, byte cr, byte k) + where TPixel : struct, IPixel + { + int c = (MaxSample - (y + CrRTable[cr])).Clamp(0, 255); + + // The values for the G calculation are left scaled up, since we must add them together before rounding. + int m = (MaxSample - (y + RightShift(CbGTable[cb] + CrGTable[cr]))).Clamp(0, 255); + + int cy = (MaxSample - (y + CbBTable[cb])).Clamp(0, 255); + + byte r = (byte)((c * k) / MaxSample); + byte g = (byte)((m * k) / MaxSample); + byte b = (byte)((cy * k) / MaxSample); + + packed.PackFromRgba32(new Rgba32(r, g, b, MaxSample)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Fix(float x) { diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs new file mode 100644 index 000000000..08b42891d --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegConstants.cs @@ -0,0 +1,357 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +{ + /// + /// Contains jpeg constant values + /// + internal static class PdfJsJpegConstants + { + /// + /// Contains marker specific constants + /// + public static class Markers + { + /// + /// The prefix used for all markers. + /// + public const byte Prefix = 0xFF; + + /// + /// The Start of Image marker + /// + public const ushort SOI = 0xFFD8; + + /// + /// The End of Image marker + /// + public const ushort EOI = 0xFFD9; + + /// + /// Application specific marker for marking the jpeg format. + /// + /// + public const ushort APP0 = 0xFFE0; + + /// + /// Application specific marker for marking where to store metadata. + /// + public const ushort APP1 = 0xFFE1; + + /// + /// Application specific marker for marking where to store ICC profile information. + /// + public const ushort APP2 = 0xFFE2; + + /// + /// Application specific marker. + /// + public const ushort APP3 = 0xFFE3; + + /// + /// Application specific marker. + /// + public const ushort APP4 = 0xFFE4; + + /// + /// Application specific marker. + /// + public const ushort APP5 = 0xFFE5; + + /// + /// Application specific marker. + /// + public const ushort APP6 = 0xFFE6; + + /// + /// Application specific marker. + /// + public const ushort APP7 = 0xFFE7; + + /// + /// Application specific marker. + /// + public const ushort APP8 = 0xFFE8; + + /// + /// Application specific marker. + /// + public const ushort APP9 = 0xFFE9; + + /// + /// Application specific marker. + /// + public const ushort APP10 = 0xFFEA; + + /// + /// Application specific marker. + /// + public const ushort APP11 = 0xFFEB; + + /// + /// Application specific marker. + /// + public const ushort APP12 = 0xFFEC; + + /// + /// Application specific marker. + /// + public const ushort APP13 = 0xFFED; + + /// + /// Application specific marker used by Adobe for storing encoding information for DCT filters. + /// + public const ushort APP14 = 0xFFEE; + + /// + /// Application specific marker used by GraphicConverter to store JPEG quality. + /// + public const ushort APP15 = 0xFFEF; + + /// + /// The text comment marker + /// + public const ushort COM = 0xFFFE; + + /// + /// Define Quantization Table(s) marker + /// + /// Specifies one or more quantization tables. + /// + /// + public const ushort DQT = 0xFFDB; + + /// + /// Start of Frame (baseline DCT) + /// + /// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components, + /// and component subsampling (e.g., 4:2:0). + /// + /// + public const ushort SOF0 = 0xFFC0; + + /// + /// Start Of Frame (Extended Sequential DCT) + /// + /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, + /// and component subsampling (e.g., 4:2:0). + /// + /// + public const ushort SOF1 = 0xFFC1; + + /// + /// Start Of Frame (progressive DCT) + /// + /// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components, + /// and component subsampling (e.g., 4:2:0). + /// + /// + public const ushort SOF2 = 0xFFC2; + + /// + /// Define Huffman Table(s) + /// + /// Specifies one or more Huffman tables. + /// + /// + public const ushort DHT = 0xFFC4; + + /// + /// Define Restart Interval + /// + /// Specifies the interval between RSTn markers, in macroblocks.This marker is followed by two bytes indicating the fixed size so it can be treated like any other variable size segment. + /// + /// + public const ushort DRI = 0xFFDD; + + /// + /// Start of Scan + /// + /// Begins a top-to-bottom scan of the image. In baseline DCT JPEG images, there is generally a single scan. + /// Progressive DCT JPEG images usually contain multiple scans. This marker specifies which slice of data it + /// will contain, and is immediately followed by entropy-coded data. + /// + /// + public const ushort SOS = 0xFFDA; + + /// + /// Define First Restart + /// + /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker. + /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7. + /// + /// + public const ushort RST0 = 0xFFD0; + + /// + /// Define Eigth Restart + /// + /// Inserted every r macroblocks, where r is the restart interval set by a DRI marker. + /// Not used if there was no DRI marker. The low three bits of the marker code cycle in value from 0 to 7. + /// + /// + public const ushort RST7 = 0xFFD7; + + /// + /// Contains JFIF specific markers + /// + public static class JFif + { + /// + /// Represents J in ASCII + /// + public const byte J = 0x4A; + + /// + /// Represents F in ASCII + /// + public const byte F = 0x46; + + /// + /// Represents I in ASCII + /// + public const byte I = 0x49; + + /// + /// Represents the null "0" marker + /// + public const byte Null = 0x0; + } + + /// + /// Contains Adobe specific markers + /// + public static class Adobe + { + /// + /// Represents A in ASCII + /// + public const byte A = 0x41; + + /// + /// Represents d in ASCII + /// + public const byte D = 0x64; + + /// + /// Represents b in ASCII + /// + public const byte O = 0x6F; + + /// + /// Represents b in ASCII + /// + public const byte B = 0x62; + + /// + /// Represents e in ASCII + /// + public const byte E = 0x65; + + /// + /// The color transform is unknown.(RGB or CMYK) + /// + public const byte ColorTransformUnknown = 0; + + /// + /// The color transform is YCbCr (luminance, red chroma, blue chroma) + /// + public const byte ColorTransformYCbCr = 1; + + /// + /// The color transform is YCCK (luminance, red chroma, blue chroma, keyline) + /// + public const byte ColorTransformYcck = 2; + } + + /// + /// Contains EXIF specific markers + /// + public static class Exif + { + /// + /// Represents E in ASCII + /// + public const byte E = 0x45; + + /// + /// Represents x in ASCII + /// + public const byte X = 0x78; + + /// + /// Represents i in ASCII + /// + public const byte I = 0x69; + + /// + /// Represents f in ASCII + /// + public const byte F = 0x66; + + /// + /// Represents the null "0" marker + /// + public const byte Null = 0x0; + } + + /// + /// Contains ICC specific markers + /// + public static class ICC + { + /// + /// Represents I in ASCII + /// + public const byte I = 0x49; + + /// + /// Represents C in ASCII + /// + public const byte C = 0x43; + + /// + /// Represents _ in ASCII + /// + public const byte UnderScore = 0x5F; + + /// + /// Represents P in ASCII + /// + public const byte P = 0x50; + + /// + /// Represents R in ASCII + /// + public const byte R = 0x52; + + /// + /// Represents O in ASCII + /// + public const byte O = 0x4F; + + /// + /// Represents F in ASCII + /// + public const byte F = 0x46; + + /// + /// Represents L in ASCII + /// + public const byte L = 0x4C; + + /// + /// Represents E in ASCII + /// + public const byte E = 0x45; + + /// + /// Represents the null "0" marker + /// + public const byte Null = 0x0; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs new file mode 100644 index 000000000..37ce0151f --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +{ + /// + /// Image decoder for generating an image out of a jpg stream. + /// + internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions + { + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; set; } + + /// + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + Guard.NotNull(stream, nameof(stream)); + + using (var decoder = new PdfJsJpegDecoderCore(configuration, this)) + { + return decoder.Decode(stream); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs new file mode 100644 index 000000000..6c84597c9 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs @@ -0,0 +1,967 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort +{ + /// + /// Performs the jpeg decoding operation. + /// Ported from with additional fixes to handle common encoding errors + /// + internal sealed class PdfJsJpegDecoderCore : IDisposable + { +#pragma warning disable SA1401 // Fields should be private + /// + /// The global configuration + /// + private readonly Configuration configuration; + + /// + /// Gets the temporary buffer used to store bytes read from the stream. + /// + private readonly byte[] temp = new byte[2 * 16 * 4]; + + private readonly byte[] markerBuffer = new byte[2]; + + private PdfJsQuantizationTables quantizationTables; + + private PdfJsHuffmanTables dcHuffmanTables; + + private PdfJsHuffmanTables acHuffmanTables; + + private PdfJsComponentBlocks components; + + private PdfJsJpegPixelArea pixelArea; + + private ushort resetInterval; + + /// + /// Whether the image has a EXIF header + /// + private bool isExif; + + /// + /// Contains information about the JFIF marker + /// + private PdfJsJFif jFif; + + /// + /// Contains information about the Adobe marker + /// + private PdfJsAdobe adobe; + + /// + /// Initializes static members of the class. + /// + static PdfJsJpegDecoderCore() + { + PdfJsYCbCrToRgbTables.Create(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The options. + public PdfJsJpegDecoderCore(Configuration configuration, IJpegDecoderOptions options) + { + this.configuration = configuration ?? Configuration.Default; + this.IgnoreMetadata = options.IgnoreMetadata; + } + + /// + /// Gets the frame + /// + public PdfJsFrame Frame { get; private set; } + + /// + /// Gets the image width + /// + public int ImageWidth { get; private set; } + + /// + /// Gets the image height + /// + public int ImageHeight { get; private set; } + + /// + /// Gets the number of components + /// + public int NumberOfComponents { get; private set; } + + /// + /// Gets the input stream. + /// + public Stream InputStream { get; private set; } + + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; } + + /// + /// Finds the next file marker within the byte stream. + /// + /// The buffer to read file markers to + /// The input stream + /// The + public static PdfJsFileMarker FindNextFileMarker(byte[] marker, Stream stream) + { + int value = stream.Read(marker, 0, 2); + + if (value == 0) + { + return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); + } + + if (marker[0] == PdfJsJpegConstants.Markers.Prefix) + { + // According to Section B.1.1.2: + // "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF." + while (marker[1] == PdfJsJpegConstants.Markers.Prefix) + { + int suffix = stream.ReadByte(); + if (suffix == -1) + { + return new PdfJsFileMarker(PdfJsJpegConstants.Markers.EOI, (int)stream.Length - 2); + } + + marker[1] = (byte)value; + } + + return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2)); + } + + return new PdfJsFileMarker((ushort)((marker[0] << 8) | marker[1]), (int)(stream.Position - 2), true); + } + + /// + /// Decodes the image from the specified and sets the data to image. + /// + /// The pixel format. + /// The stream, where the image should be. + /// The decoded image. + public Image Decode(Stream stream) + where TPixel : struct, IPixel + { + ImageMetaData metadata = this.ParseStream(stream); + + this.QuantizeAndInverseAllComponents(); + + var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); + this.FillPixelData(image.Frames.RootFrame); + this.AssignResolution(image); + return image; + } + + /// + public void Dispose() + { + this.Frame?.Dispose(); + this.components?.Dispose(); + this.quantizationTables?.Dispose(); + this.dcHuffmanTables?.Dispose(); + this.acHuffmanTables?.Dispose(); + this.pixelArea.Dispose(); + + // Set large fields to null. + this.Frame = null; + this.components = null; + this.quantizationTables = null; + this.dcHuffmanTables = null; + this.acHuffmanTables = null; + } + + internal ImageMetaData ParseStream(Stream stream) + { + this.InputStream = stream; + + var metadata = new ImageMetaData(); + this.ParseStream(metadata, false); + return metadata; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetBlockBufferOffset(ref PdfJsComponent component, int row, int col) + { + return 64 * (((component.BlocksPerLine + 1) * row) + col); + } + + /// + /// Parses the input stream for file markers + /// + /// Contains the metadata for an image + /// Whether to decode metadata only. + private void ParseStream(ImageMetaData metaData, bool metadataOnly) + { + // TODO: metadata only logic + // Check for the Start Of Image marker. + var fileMarker = new PdfJsFileMarker(this.ReadUint16(), 0); + if (fileMarker.Marker != PdfJsJpegConstants.Markers.SOI) + { + throw new ImageFormatException("Missing SOI marker."); + } + + ushort marker = this.ReadUint16(); + fileMarker = new PdfJsFileMarker(marker, (int)this.InputStream.Position - 2); + + this.quantizationTables = new PdfJsQuantizationTables(); + this.dcHuffmanTables = new PdfJsHuffmanTables(); + this.acHuffmanTables = new PdfJsHuffmanTables(); + + while (fileMarker.Marker != PdfJsJpegConstants.Markers.EOI) + { + // Get the marker length + int remaining = this.ReadUint16() - 2; + + switch (fileMarker.Marker) + { + case PdfJsJpegConstants.Markers.APP0: + this.ProcessApplicationHeaderMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.APP1: + this.ProcessApp1Marker(remaining, metaData); + break; + + case PdfJsJpegConstants.Markers.APP2: + this.ProcessApp2Marker(remaining, metaData); + break; + case PdfJsJpegConstants.Markers.APP3: + case PdfJsJpegConstants.Markers.APP4: + case PdfJsJpegConstants.Markers.APP5: + case PdfJsJpegConstants.Markers.APP6: + case PdfJsJpegConstants.Markers.APP7: + case PdfJsJpegConstants.Markers.APP8: + case PdfJsJpegConstants.Markers.APP9: + case PdfJsJpegConstants.Markers.APP10: + case PdfJsJpegConstants.Markers.APP11: + case PdfJsJpegConstants.Markers.APP12: + case PdfJsJpegConstants.Markers.APP13: + this.InputStream.Skip(remaining); + break; + + case PdfJsJpegConstants.Markers.APP14: + this.ProcessApp14Marker(remaining); + break; + + case PdfJsJpegConstants.Markers.APP15: + case PdfJsJpegConstants.Markers.COM: + this.InputStream.Skip(remaining); + break; + + case PdfJsJpegConstants.Markers.DQT: + this.ProcessDefineQuantizationTablesMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.SOF0: + case PdfJsJpegConstants.Markers.SOF1: + case PdfJsJpegConstants.Markers.SOF2: + this.ProcessStartOfFrameMarker(remaining, fileMarker); + break; + + case PdfJsJpegConstants.Markers.DHT: + this.ProcessDefineHuffmanTablesMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.DRI: + this.ProcessDefineRestartIntervalMarker(remaining); + break; + + case PdfJsJpegConstants.Markers.SOS: + this.ProcessStartOfScanMarker(); + break; + } + + // Read on. + fileMarker = FindNextFileMarker(this.markerBuffer, this.InputStream); + } + + this.ImageWidth = this.Frame.SamplesPerLine; + this.ImageHeight = this.Frame.Scanlines; + this.components = new PdfJsComponentBlocks { Components = new PdfJsComponent[this.Frame.ComponentCount] }; + + for (int i = 0; i < this.components.Components.Length; i++) + { + PdfJsFrameComponent frameComponent = this.Frame.Components[i]; + var component = new PdfJsComponent + { + Scale = new System.Numerics.Vector2( + frameComponent.HorizontalSamplingFactor / (float)this.Frame.MaxHorizontalFactor, + frameComponent.VerticalSamplingFactor / (float)this.Frame.MaxVerticalFactor), + BlocksPerLine = frameComponent.WidthInBlocks, + BlocksPerColumn = frameComponent.HeightInBlocks + }; + + // this.QuantizeAndInverseComponentData(ref component, frameComponent); + this.components.Components[i] = component; + } + + this.NumberOfComponents = this.components.Components.Length; + } + + internal void QuantizeAndInverseAllComponents() + { + for (int i = 0; i < this.components.Components.Length; i++) + { + PdfJsFrameComponent frameComponent = this.Frame.Components[i]; + PdfJsComponent component = this.components.Components[i]; + + this.QuantizeAndInverseComponentData(component, frameComponent); + } + } + + /// + /// Fills the given image with the color data + /// + /// The pixel format. + /// The image + private void FillPixelData(ImageFrame image) + where TPixel : struct, IPixel + { + if (this.NumberOfComponents > 4) + { + throw new ImageFormatException($"Unsupported color mode. Max components 4; found {this.NumberOfComponents}"); + } + + this.pixelArea = new PdfJsJpegPixelArea(image.Width, image.Height, this.NumberOfComponents); + this.pixelArea.LinearizeBlockData(this.components, image.Width, image.Height); + + if (this.NumberOfComponents == 1) + { + this.FillGrayScaleImage(image); + return; + } + + if (this.NumberOfComponents == 3) + { + if (this.adobe.Equals(default(PdfJsAdobe)) || this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYCbCr) + { + this.FillYCbCrImage(image); + } + else if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformUnknown) + { + this.FillRgbImage(image); + } + } + + if (this.NumberOfComponents == 4) + { + if (this.adobe.ColorTransform == PdfJsJpegConstants.Markers.Adobe.ColorTransformYcck) + { + this.FillYcckImage(image); + } + else + { + this.FillCmykImage(image); + } + } + } + + /// + /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata. + /// + /// The pixel format. + /// The image to assign the resolution to. + private void AssignResolution(Image image) + where TPixel : struct, IPixel + { + if (this.isExif) + { + ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); + double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; + double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; + + if (horizontalValue > 0 && verticalValue > 0) + { + image.MetaData.HorizontalResolution = horizontalValue; + image.MetaData.VerticalResolution = verticalValue; + } + } + else if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0) + { + image.MetaData.HorizontalResolution = this.jFif.XDensity; + image.MetaData.VerticalResolution = this.jFif.YDensity; + } + } + + /// + /// Processes the application header containing the JFIF identifier plus extra data. + /// + /// The remaining bytes in the segment block. + private void ProcessApplicationHeaderMarker(int remaining) + { + if (remaining < 5) + { + // Skip the application header length + this.InputStream.Skip(remaining); + return; + } + + this.InputStream.Read(this.temp, 0, 13); + remaining -= 13; + + bool isJfif = this.temp[0] == PdfJsJpegConstants.Markers.JFif.J && + this.temp[1] == PdfJsJpegConstants.Markers.JFif.F && + this.temp[2] == PdfJsJpegConstants.Markers.JFif.I && + this.temp[3] == PdfJsJpegConstants.Markers.JFif.F && + this.temp[4] == PdfJsJpegConstants.Markers.JFif.Null; + + if (isJfif) + { + this.jFif = new PdfJsJFif + { + MajorVersion = this.temp[5], + MinorVersion = this.temp[6], + DensityUnits = this.temp[7], + XDensity = (short)((this.temp[8] << 8) | this.temp[9]), + YDensity = (short)((this.temp[10] << 8) | this.temp[11]) + }; + } + + // TODO: thumbnail + if (remaining > 0) + { + this.InputStream.Skip(remaining); + } + } + + /// + /// Processes the App1 marker retrieving any stored metadata + /// + /// The remaining bytes in the segment block. + /// The image. + private void ProcessApp1Marker(int remaining, ImageMetaData metadata) + { + if (remaining < 6 || this.IgnoreMetadata) + { + // Skip the application header length + this.InputStream.Skip(remaining); + return; + } + + byte[] profile = new byte[remaining]; + this.InputStream.Read(profile, 0, remaining); + + if (profile[0] == PdfJsJpegConstants.Markers.Exif.E && + profile[1] == PdfJsJpegConstants.Markers.Exif.X && + profile[2] == PdfJsJpegConstants.Markers.Exif.I && + profile[3] == PdfJsJpegConstants.Markers.Exif.F && + profile[4] == PdfJsJpegConstants.Markers.Exif.Null && + profile[5] == PdfJsJpegConstants.Markers.Exif.Null) + { + this.isExif = true; + metadata.ExifProfile = new ExifProfile(profile); + } + } + + /// + /// Processes the App2 marker retrieving any stored ICC profile information + /// + /// The remaining bytes in the segment block. + /// The image. + private void ProcessApp2Marker(int remaining, ImageMetaData metadata) + { + // Length is 14 though we only need to check 12. + const int Icclength = 14; + if (remaining < Icclength || this.IgnoreMetadata) + { + this.InputStream.Skip(remaining); + return; + } + + byte[] identifier = new byte[Icclength]; + this.InputStream.Read(identifier, 0, Icclength); + remaining -= Icclength; // We have read it by this point + + if (identifier[0] == PdfJsJpegConstants.Markers.ICC.I && + identifier[1] == PdfJsJpegConstants.Markers.ICC.C && + identifier[2] == PdfJsJpegConstants.Markers.ICC.C && + identifier[3] == PdfJsJpegConstants.Markers.ICC.UnderScore && + identifier[4] == PdfJsJpegConstants.Markers.ICC.P && + identifier[5] == PdfJsJpegConstants.Markers.ICC.R && + identifier[6] == PdfJsJpegConstants.Markers.ICC.O && + identifier[7] == PdfJsJpegConstants.Markers.ICC.F && + identifier[8] == PdfJsJpegConstants.Markers.ICC.I && + identifier[9] == PdfJsJpegConstants.Markers.ICC.L && + identifier[10] == PdfJsJpegConstants.Markers.ICC.E && + identifier[11] == PdfJsJpegConstants.Markers.ICC.Null) + { + byte[] profile = new byte[remaining]; + this.InputStream.Read(profile, 0, remaining); + + if (metadata.IccProfile == null) + { + metadata.IccProfile = new IccProfile(profile); + } + else + { + metadata.IccProfile.Extend(profile); + } + } + else + { + // Not an ICC profile we can handle. Skip the remaining bytes so we can carry on and ignore this. + this.InputStream.Skip(remaining); + } + } + + /// + /// Processes the application header containing the Adobe identifier + /// which stores image encoding information for DCT filters. + /// + /// The remaining bytes in the segment block. + private void ProcessApp14Marker(int remaining) + { + if (remaining < 12) + { + // Skip the application header length + this.InputStream.Skip(remaining); + return; + } + + this.InputStream.Read(this.temp, 0, 12); + remaining -= 12; + + bool isAdobe = this.temp[0] == PdfJsJpegConstants.Markers.Adobe.A && + this.temp[1] == PdfJsJpegConstants.Markers.Adobe.D && + this.temp[2] == PdfJsJpegConstants.Markers.Adobe.O && + this.temp[3] == PdfJsJpegConstants.Markers.Adobe.B && + this.temp[4] == PdfJsJpegConstants.Markers.Adobe.E; + + if (isAdobe) + { + this.adobe = new PdfJsAdobe + { + DCTEncodeVersion = (short)((this.temp[5] << 8) | this.temp[6]), + APP14Flags0 = (short)((this.temp[7] << 8) | this.temp[8]), + APP14Flags1 = (short)((this.temp[9] << 8) | this.temp[10]), + ColorTransform = this.temp[11] + }; + } + + if (remaining > 0) + { + this.InputStream.Skip(remaining); + } + } + + /// + /// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1. + /// + /// The remaining bytes in the segment block. + /// + /// Thrown if the tables do not match the header + /// + private void ProcessDefineQuantizationTablesMarker(int remaining) + { + while (remaining > 0) + { + bool done = false; + remaining--; + int quantizationTableSpec = this.InputStream.ReadByte(); + + switch (quantizationTableSpec >> 4) + { + case 0: + { + // 8 bit values + if (remaining < 64) + { + done = true; + break; + } + + this.InputStream.Read(this.temp, 0, 64); + remaining -= 64; + + Span tableSpan = this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15); + for (int j = 0; j < 64; j++) + { + tableSpan[PdfJsQuantizationTables.DctZigZag[j]] = this.temp[j]; + } + } + + break; + case 1: + { + // 16 bit values + if (remaining < 128) + { + done = true; + break; + } + + this.InputStream.Read(this.temp, 0, 128); + remaining -= 128; + + Span tableSpan = this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec & 15); + for (int j = 0; j < 64; j++) + { + tableSpan[PdfJsQuantizationTables.DctZigZag[j]] = (short)((this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]); + } + } + + break; + default: + throw new ImageFormatException("Bad Tq index value"); + } + + if (done) + { + break; + } + } + + if (remaining != 0) + { + throw new ImageFormatException("DQT has wrong length"); + } + } + + /// + /// Processes the Start of Frame marker. Specified in section B.2.2. + /// + /// The remaining bytes in the segment block. + /// The current frame marker. + private void ProcessStartOfFrameMarker(int remaining, PdfJsFileMarker frameMarker) + { + if (this.Frame != null) + { + throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported."); + } + + this.InputStream.Read(this.temp, 0, remaining); + + this.Frame = new PdfJsFrame + { + Extended = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF1, + Progressive = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF2, + Precision = this.temp[0], + Scanlines = (short)((this.temp[1] << 8) | this.temp[2]), + SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]), + ComponentCount = this.temp[5] + }; + + int maxH = 0; + int maxV = 0; + int index = 6; + + // No need to pool this. They max out at 4 + this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; + this.Frame.Components = new PdfJsFrameComponent[this.Frame.ComponentCount]; + + for (int i = 0; i < this.Frame.Components.Length; i++) + { + int h = this.temp[index + 1] >> 4; + int v = this.temp[index + 1] & 15; + + if (maxH < h) + { + maxH = h; + } + + if (maxV < v) + { + maxV = v; + } + + var component = new PdfJsFrameComponent(this.Frame, this.temp[index], h, v, this.temp[index + 2], i); + + this.Frame.Components[i] = component; + this.Frame.ComponentIds[i] = component.Id; + + index += 3; + } + + this.Frame.MaxHorizontalFactor = maxH; + this.Frame.MaxVerticalFactor = maxV; + this.Frame.InitComponents(); + } + + /// + /// Processes a Define Huffman Table marker, and initializes a huffman + /// struct from its contents. Specified in section B.2.4.2. + /// + /// The remaining bytes in the segment block. + private void ProcessDefineHuffmanTablesMarker(int remaining) + { + if (remaining < 17) + { + throw new ImageFormatException($"DHT has wrong length: {remaining}"); + } + + using (var huffmanData = Buffer.CreateClean(256)) + { + for (int i = 2; i < remaining;) + { + byte huffmanTableSpec = (byte)this.InputStream.ReadByte(); + this.InputStream.Read(huffmanData.Array, 0, 16); + + using (var codeLengths = Buffer.CreateClean(17)) + { + int codeLengthSum = 0; + + for (int j = 1; j < 17; j++) + { + codeLengthSum += codeLengths[j] = huffmanData[j - 1]; + } + + using (var huffmanValues = Buffer.CreateClean(256)) + { + this.InputStream.Read(huffmanValues.Array, 0, codeLengthSum); + + i += 17 + codeLengthSum; + + this.BuildHuffmanTable( + huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables, + huffmanTableSpec & 15, + codeLengths.Array, + huffmanValues.Array); + } + } + } + } + } + + /// + /// Processes the DRI (Define Restart Interval Marker) Which specifies the interval between RSTn markers, in + /// macroblocks + /// + /// The remaining bytes in the segment block. + private void ProcessDefineRestartIntervalMarker(int remaining) + { + if (remaining != 2) + { + throw new ImageFormatException($"DRI has wrong length: {remaining}"); + } + + this.resetInterval = this.ReadUint16(); + } + + /// + /// Processes the SOS (Start of scan marker). + /// + private void ProcessStartOfScanMarker() + { + int selectorsCount = this.InputStream.ReadByte(); + int componentIndex = -1; + for (int i = 0; i < selectorsCount; i++) + { + componentIndex = -1; + int selector = this.InputStream.ReadByte(); + + for (int j = 0; j < this.Frame.ComponentIds.Length; j++) + { + byte id = this.Frame.ComponentIds[j]; + if (selector == id) + { + componentIndex = j; + } + } + + if (componentIndex < 0) + { + throw new ImageFormatException("Unknown component selector"); + } + + ref PdfJsFrameComponent component = ref this.Frame.Components[componentIndex]; + int tableSpec = this.InputStream.ReadByte(); + component.DCHuffmanTableId = tableSpec >> 4; + component.ACHuffmanTableId = tableSpec & 15; + } + + this.InputStream.Read(this.temp, 0, 3); + + int spectralStart = this.temp[0]; + int spectralEnd = this.temp[1]; + int successiveApproximation = this.temp[2]; + var scanDecoder = default(PdfJsScanDecoder); + + scanDecoder.DecodeScan( + this.Frame, + this.InputStream, + this.dcHuffmanTables, + this.acHuffmanTables, + this.Frame.Components, + componentIndex, + selectorsCount, + this.resetInterval, + spectralStart, + spectralEnd, + successiveApproximation >> 4, + successiveApproximation & 15); + } + + /// + /// Build the data for the given component + /// + /// The component + /// The frame component + private void QuantizeAndInverseComponentData(PdfJsComponent component, PdfJsFrameComponent frameComponent) + { + int blocksPerLine = component.BlocksPerLine; + int blocksPerColumn = component.BlocksPerColumn; + using (var computationBuffer = Buffer.CreateClean(64)) + using (var multiplicationBuffer = Buffer.CreateClean(64)) + { + Span quantizationTable = this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex); + Span computationBufferSpan = computationBuffer; + + // For AA&N IDCT method, multiplier are equal to quantization + // coefficients scaled by scalefactor[row]*scalefactor[col], where + // scalefactor[0] = 1 + // scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + // For integer operation, the multiplier table is to be scaled by 12. + Span multiplierSpan = multiplicationBuffer; + + // for (int i = 0; i < 64; i++) + // { + // multiplierSpan[i] = (short)IDCT.Descale(quantizationTable[i] * IDCT.Aanscales[i], 12); + // } + for (int blockRow = 0; blockRow < blocksPerColumn; blockRow++) + { + for (int blockCol = 0; blockCol < blocksPerLine; blockCol++) + { + int offset = GetBlockBufferOffset(ref component, blockRow, blockCol); + PdfJsIDCT.QuantizeAndInverse(frameComponent, offset, ref computationBufferSpan, ref quantizationTable); + } + } + } + + component.Output = frameComponent.BlockData; + } + + /// + /// Builds the huffman tables + /// + /// The tables + /// The table index + /// The codelengths + /// The values + private void BuildHuffmanTable(PdfJsHuffmanTables tables, int index, byte[] codeLengths, byte[] values) + { + tables[index] = new PdfJsHuffmanTable(codeLengths, values); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillGrayScaleImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + + for (int x = 0; x < image.Width; x++) + { + ref byte luminance = ref areaRowSpan[x]; + ref TPixel pixel = ref imageRowSpan[x]; + var rgba = new Rgba32(luminance, luminance, luminance); + pixel.PackFromRgba32(rgba); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillYCbCrImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + for (int x = 0, o = 0; x < image.Width; x++, o += 3) + { + ref byte yy = ref areaRowSpan[o]; + ref byte cb = ref areaRowSpan[o + 1]; + ref byte cr = ref areaRowSpan[o + 2]; + ref TPixel pixel = ref imageRowSpan[x]; + PdfJsYCbCrToRgbTables.PackYCbCr(ref pixel, yy, cb, cr); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillYcckImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + for (int x = 0, o = 0; x < image.Width; x++, o += 4) + { + ref byte yy = ref areaRowSpan[o]; + ref byte cb = ref areaRowSpan[o + 1]; + ref byte cr = ref areaRowSpan[o + 2]; + ref byte k = ref areaRowSpan[o + 3]; + + ref TPixel pixel = ref imageRowSpan[x]; + PdfJsYCbCrToRgbTables.PackYccK(ref pixel, yy, cb, cr, k); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillCmykImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + for (int x = 0, o = 0; x < image.Width; x++, o += 4) + { + ref byte c = ref areaRowSpan[o]; + ref byte m = ref areaRowSpan[o + 1]; + ref byte cy = ref areaRowSpan[o + 2]; + ref byte k = ref areaRowSpan[o + 3]; + + byte r = (byte)((c * k) / 255); + byte g = (byte)((m * k) / 255); + byte b = (byte)((cy * k) / 255); + + ref TPixel pixel = ref imageRowSpan[x]; + var rgba = new Rgba32(r, g, b); + pixel.PackFromRgba32(rgba); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FillRgbImage(ImageFrame image) + where TPixel : struct, IPixel + { + for (int y = 0; y < image.Height; y++) + { + Span imageRowSpan = image.GetPixelRowSpan(y); + Span areaRowSpan = this.pixelArea.GetRowSpan(y); + + PixelOperations.Instance.PackFromRgb24Bytes(areaRowSpan, imageRowSpan, image.Width); + } + } + + /// + /// Reads a from the stream advancing it by two bytes + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ushort ReadUint16() + { + this.InputStream.Read(this.markerBuffer, 0, 2); + return (ushort)((this.markerBuffer[0] << 8) | this.markerBuffer[1]); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpan.cs b/src/ImageSharp/Formats/Jpeg/Utils/MutableSpan.cs deleted file mode 100644 index 99d1c3e04..000000000 --- a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpan.cs +++ /dev/null @@ -1,100 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System.Runtime.CompilerServices; - - /// - /// Like corefxlab Span, but with an AddOffset() method for efficiency. - /// TODO: When Span will be official, consider replacing this class! - /// - /// - /// https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs - /// - /// The type of the data in the span - internal struct MutableSpan - { - /// - /// Data - /// - public T[] Data; - - /// - /// Offset - /// - public int Offset; - - /// - /// Initializes a new instance of the struct. - /// - /// The size of the span - /// The offset (defaults to 0) - public MutableSpan(int size, int offset = 0) - { - this.Data = new T[size]; - this.Offset = offset; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The data - /// The offset (defaults to 0) - public MutableSpan(T[] data, int offset = 0) - { - this.Data = data; - this.Offset = offset; - } - - /// - /// Gets the total count of data - /// - public int TotalCount => this.Data.Length - this.Offset; - - /// - /// Index into the data - /// - /// The data - /// The value at the specified index - public T this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.Data[idx + this.Offset]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.Data[idx + this.Offset] = value; - } - } - - public static implicit operator MutableSpan(T[] data) => new MutableSpan(data, 0); - - /// - /// Slice the data - /// - /// The offset - /// The new - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MutableSpan Slice(int offset) - { - return new MutableSpan(this.Data, this.Offset + offset); - } - - /// - /// Add to the offset - /// - /// The additional offset - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddOffset(int offset) - { - this.Offset += offset; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpanExtensions.cs b/src/ImageSharp/Formats/Jpeg/Utils/MutableSpanExtensions.cs deleted file mode 100644 index 45ecfc092..000000000 --- a/src/ImageSharp/Formats/Jpeg/Utils/MutableSpanExtensions.cs +++ /dev/null @@ -1,164 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - /// - /// MutableSpan Extensions - /// - internal static class MutableSpanExtensions - { - /// - /// Slice - /// - /// The type of the data in the span - /// The data array - /// The offset - /// The new - public static MutableSpan Slice(this T[] array, int offset) => new MutableSpan(array, offset); - - /// - /// Save to a Vector4 - /// - /// The data - /// The vector to save to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SaveTo(this MutableSpan data, ref Vector4 v) - { - v.X = data[0]; - v.Y = data[1]; - v.Z = data[2]; - v.W = data[3]; - } - - /// - /// Save to a Vector4 - /// - /// The data - /// The vector to save to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SaveTo(this MutableSpan data, ref Vector4 v) - { - v.X = data[0]; - v.Y = data[1]; - v.Z = data[2]; - v.W = data[3]; - } - - /// - /// Load from Vector4 - /// - /// The data - /// The vector to load from - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void LoadFrom(this MutableSpan data, ref Vector4 v) - { - data[0] = v.X; - data[1] = v.Y; - data[2] = v.Z; - data[3] = v.W; - } - - /// - /// Load from Vector4 - /// - /// The data - /// The vector to load from - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void LoadFrom(this MutableSpan data, ref Vector4 v) - { - data[0] = (int)v.X; - data[1] = (int)v.Y; - data[2] = (int)v.Z; - data[3] = (int)v.W; - } - - /// - /// Converts all int values of src to float - /// - /// Source - /// A new with float values - public static MutableSpan ConvertToFloat32MutableSpan(this MutableSpan src) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = (float)src[i]; - } - - return result; - } - - /// - /// Converts all float values of src to int - /// - /// Source - /// A new with float values - public static MutableSpan ConvertToInt32MutableSpan(this MutableSpan src) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = (int)src[i]; - } - - return result; - } - - /// - /// Add a scalar to all values of src - /// - /// The source - /// The scalar value to add - /// A new instance of - public static MutableSpan AddScalarToAllValues(this MutableSpan src, float scalar) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = src[i] + scalar; - } - - return result; - } - - /// - /// Add a scalar to all values of src - /// - /// The source - /// The scalar value to add - /// A new instance of - public static MutableSpan AddScalarToAllValues(this MutableSpan src, int scalar) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = src[i] + scalar; - } - - return result; - } - - /// - /// Copy all values in src to a new instance - /// - /// Element type - /// The source - /// A new instance of - public static MutableSpan Copy(this MutableSpan src) - { - MutableSpan result = new MutableSpan(src.TotalCount); - for (int i = 0; i < src.TotalCount; i++) - { - result[i] = src[i]; - } - - return result; - } - } -} diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index db2189b3c..d66aa06dd 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Average filter uses the average of the two neighboring pixels (left and above) to predict /// the value of a pixel. diff --git a/src/ImageSharp/Formats/Png/Filters/FilterType.cs b/src/ImageSharp/Formats/Png/Filters/FilterType.cs index 0eddf08f2..83a005380 100644 --- a/src/ImageSharp/Formats/Png/Filters/FilterType.cs +++ b/src/ImageSharp/Formats/Png/Filters/FilterType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png.Filters { /// /// Provides enumeration of the various PNG filter types. diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs index ee77d1a5e..0164ceafa 100644 --- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The None filter, the scanline is transmitted unmodified; it is only necessary to /// insert a filter type byte before the data. diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index f0f5ae099..630aa56ee 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left), /// then chooses as predictor the neighboring pixel closest to the computed value. diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index f81122ad2..43607dcdd 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Sub filter transmits the difference between each byte and the value of the corresponding byte /// of the prior pixel. diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index b89a3c5fc..12a566e32 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Formats.Png.Filters +{ /// /// The Up filter is just like the Sub filter except that the pixel immediately above the current pixel, /// rather than just to its left, is used as the predictor. diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs index de163ba7e..e51cc084b 100644 --- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// The optioas for decoding png images /// diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 8f0a4cd82..6b4b05e31 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// The options availible for manipulating the encoder pipeline /// diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index c81738576..10970fc16 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - - using Formats; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,14 +21,9 @@ namespace ImageSharp /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsPng(this Image source, Stream stream) + public static void SaveAsPng(this Image source, Stream stream) where TPixel : struct, IPixel - { - return SaveAsPng(source, stream, null); - } + => SaveAsPng(source, stream, null); /// /// Saves the image to the given stream with the png format. @@ -40,16 +33,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// - /// The . - /// - public static Image SaveAsPng(this Image source, Stream stream, PngEncoder encoder) + public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) where TPixel : struct, IPixel - { - encoder = encoder ?? new PngEncoder(); - encoder.Encode(source, stream); - - return source; - } + => source.Save(stream, encoder ?? source.GetConfiguration().FindEncoder(ImageFormats.Png)); } } diff --git a/src/ImageSharp/Formats/Png/PngChunk.cs b/src/ImageSharp/Formats/Png/PngChunk.cs index 3d769057c..f90def5b3 100644 --- a/src/ImageSharp/Formats/Png/PngChunk.cs +++ b/src/ImageSharp/Formats/Png/PngChunk.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Stores header information about a chunk. diff --git a/src/ImageSharp/Formats/Png/PngChunkTypes.cs b/src/ImageSharp/Formats/Png/PngChunkTypes.cs index 72486b50d..e22f4f0e7 100644 --- a/src/ImageSharp/Formats/Png/PngChunkTypes.cs +++ b/src/ImageSharp/Formats/Png/PngChunkTypes.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Contains a list of possible chunk type identifiers. diff --git a/src/ImageSharp/Formats/Png/PngColorType.cs b/src/ImageSharp/Formats/Png/PngColorType.cs index 421e27ba3..fc376ca16 100644 --- a/src/ImageSharp/Formats/Png/PngColorType.cs +++ b/src/ImageSharp/Formats/Png/PngColorType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Provides enumeration of available PNG color types. diff --git a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs index bb1c2086c..9346f7567 100644 --- a/src/ImageSharp/Formats/Png/PngConfigurationModule.cs +++ b/src/ImageSharp/Formats/Png/PngConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Registers the image encoders, decoders and mime type detectors for the png format. diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs index 84e76a341..8b4ad39f2 100644 --- a/src/ImageSharp/Formats/Png/PngConstants.cs +++ b/src/ImageSharp/Formats/Png/PngConstants.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.Text; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Defines png constants defined in the specification. /// diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 61a8cb212..739fd6051 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Encoder for generating an image out of a png encoded stream. /// @@ -44,7 +42,7 @@ namespace ImageSharp.Formats public Encoding TextEncoding { get; set; } = PngConstants.DefaultEncoding; /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// /// The pixel format. /// The configuration for the image. diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 6429b68fa..4fd57c278 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -1,22 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png.Filters; +using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.ComparableExtensions; + +namespace SixLabors.ImageSharp.Formats.Png { - using System; - using System.Buffers; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - using System.Text; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - using static ComparableExtensions; - /// /// Performs the png decoding operation. /// @@ -84,6 +85,16 @@ namespace ImageSharp.Formats /// private readonly Configuration configuration; + /// + /// Gets the encoding to use + /// + private readonly Encoding textEncoding; + + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + private readonly bool ignoreMetadata; + /// /// The stream to decode from. /// @@ -155,14 +166,19 @@ namespace ImageSharp.Formats private PngColorType pngColorType; /// - /// Gets the encoding to use + /// Represents any color in an Rgb24 encoded png that should be transparent /// - private Encoding textEncoding; + private Rgb24 rgb24Trans; /// - /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// Represents any color in a Grayscale encoded png that should be transparent /// - private bool ignoreMetadata; + private byte intensityTrans; + + /// + /// Whether the image has transparency chunk and markers were decoded + /// + private bool hasTrans; /// /// Initializes a new instance of the class. @@ -220,7 +236,7 @@ namespace ImageSharp.Formats } deframeStream.AllocateNewBytes(currentChunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image); + this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); stream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: @@ -232,6 +248,7 @@ namespace ImageSharp.Formats byte[] alpha = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); this.paletteAlpha = alpha; + this.AssignTransparentMarkers(alpha); break; case PngChunkTypes.Text: this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); @@ -300,6 +317,11 @@ namespace ImageSharp.Formats return result; } + /// + /// Returns a value indicating whether the given chunk is critical to decoding + /// + /// The chunk + /// The private static bool IsCriticalChunk(PngChunk chunk) { return @@ -309,6 +331,17 @@ namespace ImageSharp.Formats chunk.Type == PngChunkTypes.End; } + /// + /// Reads an integer value from 2 consecutive bytes in LSB order + /// + /// The source buffer + /// THe offset + /// The + public static int ReadIntFrom2Bytes(byte[] buffer, int offset) + { + return ((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF); + } + /// /// Reads the data chunk containing physical dimension data. /// @@ -409,7 +442,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing data. /// The pixel data. - private void ReadScanlines(Stream dataStream, Image image) + private void ReadScanlines(Stream dataStream, ImageFrame image) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) @@ -428,7 +461,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The compressed pixel data stream. /// The image to decode to. - private void DecodePixelData(Stream compressedStream, Image image) + private void DecodePixelData(Stream compressedStream, ImageFrame image) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -486,7 +519,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The compressed pixel data stream. /// The current image. - private void DecodeInterlacedPixelData(Stream compressedStream, Image image) + private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image) where TPixel : struct, IPixel { while (true) @@ -547,7 +580,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - Span rowSpan = image.GetRowSpan(this.currentRow); + Span rowSpan = image.GetPixelRowSpan(this.currentRow); this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); Swap(ref this.scanline, ref this.previousScanline); @@ -576,11 +609,11 @@ namespace ImageSharp.Formats /// The pixel format. /// The de-filtered scanline /// The image - private void ProcessDefilteredScanline(byte[] defilteredScanline, Image pixels) + private void ProcessDefilteredScanline(byte[] defilteredScanline, ImageFrame pixels) where TPixel : struct, IPixel { var color = default(TPixel); - Span rowSpan = pixels.GetRowSpan(this.currentRow); + Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); // Trim the first marker byte from the buffer var scanlineBuffer = new Span(defilteredScanline, 1); @@ -590,10 +623,19 @@ namespace ImageSharp.Formats case PngColorType.Grayscale: int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); Span newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); + for (int x = 0; x < this.header.Width; x++) { byte intensity = (byte)(newScanline1[x] * factor); - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + if (this.hasTrans && intensity == this.intensityTrans) + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 0)); + } + else + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + } + rowSpan[x] = color; } @@ -622,19 +664,60 @@ namespace ImageSharp.Formats case PngColorType.Rgb: - if (this.header.BitDepth == 16) + if (!this.hasTrans) { - int length = this.header.Width * 3; - using (var compressed = new Buffer(length)) + if (this.header.BitDepth == 16) { - // TODO: Should we use pack from vector here instead? - this.From16BitTo8Bit(scanlineBuffer, compressed, length); - PixelOperations.Instance.PackFromRgb24Bytes(compressed, rowSpan, this.header.Width); + int length = this.header.Width * 3; + using (var compressed = new Buffer(length)) + { + // TODO: Should we use pack from vector here instead? + this.From16BitTo8Bit(scanlineBuffer, compressed, length); + PixelOperations.Instance.PackFromRgb24Bytes(compressed, rowSpan, this.header.Width); + } + } + else + { + PixelOperations.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width); } } else { - PixelOperations.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width); + if (this.header.BitDepth == 16) + { + int length = this.header.Width * 3; + using (var compressed = new Buffer(length)) + { + // TODO: Should we use pack from vector here instead? + this.From16BitTo8Bit(scanlineBuffer, compressed, length); + + Span rgb24Span = compressed.Span.NonPortableCast(); + for (int x = 0; x < this.header.Width; x++) + { + ref Rgb24 rgb24 = ref rgb24Span[x]; + var rgba32 = default(Rgba32); + rgba32.Rgb = rgb24; + rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255); + + color.PackFromRgba32(rgba32); + rowSpan[x] = color; + } + } + } + else + { + Span rgb24Span = scanlineBuffer.NonPortableCast(); + for (int x = 0; x < this.header.Width; x++) + { + ref Rgb24 rgb24 = ref rgb24Span[x]; + var rgba32 = default(Rgba32); + rgba32.Rgb = rgb24; + rgba32.A = (byte)(rgb24.Equals(this.rgb24Trans) ? 0 : 255); + + color.PackFromRgba32(rgba32); + rowSpan[x] = color; + } + } } break; @@ -675,6 +758,33 @@ namespace ImageSharp.Formats } } + /// + /// Decodes and assigns marker colors that identify transparent pixels in non indexed images + /// + /// The aplha tRNS array + private void AssignTransparentMarkers(byte[] alpha) + { + if (this.pngColorType == PngColorType.Rgb) + { + if (alpha.Length >= 6) + { + byte r = (byte)ReadIntFrom2Bytes(alpha, 0); + byte g = (byte)ReadIntFrom2Bytes(alpha, 2); + byte b = (byte)ReadIntFrom2Bytes(alpha, 4); + this.rgb24Trans = new Rgb24(r, g, b); + this.hasTrans = true; + } + } + else if (this.pngColorType == PngColorType.Grayscale) + { + if (alpha.Length >= 2) + { + this.intensityTrans = (byte)ReadIntFrom2Bytes(alpha, 0); + this.hasTrans = true; + } + } + } + /// /// Processes a scanline that uses a palette /// @@ -744,10 +854,19 @@ namespace ImageSharp.Formats case PngColorType.Grayscale: int factor = 255 / ((int)Math.Pow(2, this.header.BitDepth) - 1); Span newScanline1 = ToArrayByBitsLength(scanlineBuffer, this.bytesPerScanline, this.header.BitDepth); + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) { byte intensity = (byte)(newScanline1[o] * factor); - color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + if (this.hasTrans && intensity == this.intensityTrans) + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, 0)); + } + else + { + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + } + rowSpan[x] = color; } @@ -815,27 +934,60 @@ namespace ImageSharp.Formats { // TODO: Should we use pack from vector here instead? this.From16BitTo8Bit(scanlineBuffer, compressed, length); - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) - { - rgba.R = compressed[o]; - rgba.G = compressed[o + 1]; - rgba.B = compressed[o + 2]; - color.PackFromRgba32(rgba); - rowSpan[x] = color; + if (this.hasTrans) + { + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) + { + rgba.R = compressed[o]; + rgba.G = compressed[o + 1]; + rgba.B = compressed[o + 2]; + rgba.A = (byte)(this.rgb24Trans.Equals(rgba.Rgb) ? 0 : 255); + + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } + } + else + { + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 3) + { + rgba.R = compressed[o]; + rgba.G = compressed[o + 1]; + rgba.B = compressed[o + 2]; + + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } } } } else { - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) + if (this.hasTrans) { - rgba.R = scanlineBuffer[o]; - rgba.G = scanlineBuffer[o + this.bytesPerSample]; - rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)]; + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + rgba.R = scanlineBuffer[o]; + rgba.G = scanlineBuffer[o + this.bytesPerSample]; + rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)]; + rgba.A = (byte)(this.rgb24Trans.Equals(rgba.Rgb) ? 0 : 255); - color.PackFromRgba32(rgba); - rowSpan[x] = color; + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } + } + else + { + for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) + { + rgba.R = scanlineBuffer[o]; + rgba.G = scanlineBuffer[o + this.bytesPerSample]; + rgba.B = scanlineBuffer[o + (2 * this.bytesPerSample)]; + + color.PackFromRgba32(rgba); + rowSpan[x] = color; + } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index bfd82a074..6b2410e83 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; - using System.IO; - - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System.Collections.Generic; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Image encoder for writing image data to a stream in png format. /// diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index cfbd0c449..660c37187 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1,23 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +using System; +using System.Buffers; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png.Filters; +using SixLabors.ImageSharp.Formats.Png.Zlib; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +using static SixLabors.ImageSharp.ComparableExtensions; + +namespace SixLabors.ImageSharp.Formats.Png { - using System; - using System.Buffers; - using System.IO; - using System.Linq; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - - using Quantizers; - - using static ComparableExtensions; - /// /// Performs the png encoding operation. /// @@ -168,7 +166,7 @@ namespace ImageSharp.Formats /// Encodes the image to the specified stream from the . /// /// The pixel format. - /// The to encode from. + /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) where TPixel : struct, IPixel @@ -235,12 +233,12 @@ namespace ImageSharp.Formats // Collect the indexed pixel data if (this.pngColorType == PngColorType.Palette) { - this.CollectIndexedBytes(image, stream, header); + this.CollectIndexedBytes(image.Frames.RootFrame, stream, header); } this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - this.WriteDataChunks(image, stream); + this.WriteDataChunks(image.Frames.RootFrame, stream); this.WriteEndChunk(stream); stream.Flush(); } @@ -306,7 +304,7 @@ namespace ImageSharp.Formats /// The image to encode. /// The containing image data. /// The . - private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header) + private void CollectIndexedBytes(ImageFrame image, Stream stream, PngHeader header) where TPixel : struct, IPixel { // Quantize the image and get the pixels. @@ -531,7 +529,7 @@ namespace ImageSharp.Formats /// The . /// The image to encode. /// The - private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) + private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageFrame image) where TPixel : struct, IPixel { if (this.paletteSize > 256) @@ -651,7 +649,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The image. /// The stream. - private void WriteDataChunks(Image pixels, Stream stream) + private void WriteDataChunks(ImageFrame pixels, Stream stream) where TPixel : struct, IPixel { this.bytesPerScanline = this.width * this.bytesPerPixel; @@ -679,7 +677,7 @@ namespace ImageSharp.Formats { for (int y = 0; y < this.height; y++) { - Buffer r = this.EncodePixelRow(pixels.GetRowSpan(y), y); + Buffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y), y); deflateStream.Write(r.Array, 0, resultLength); Swap(ref this.rawScanline, ref this.previousScanline); diff --git a/src/ImageSharp/Formats/Png/PngFormat.cs b/src/ImageSharp/Formats/Png/PngFormat.cs index 6df2aa7c5..142f66071 100644 --- a/src/ImageSharp/Formats/Png/PngFormat.cs +++ b/src/ImageSharp/Formats/Png/PngFormat.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Registers the image encoders, decoders and mime type detectors for the png format. /// diff --git a/src/ImageSharp/Formats/Png/PngHeader.cs b/src/ImageSharp/Formats/Png/PngHeader.cs index 9cf823fa1..a70032ce3 100644 --- a/src/ImageSharp/Formats/Png/PngHeader.cs +++ b/src/ImageSharp/Formats/Png/PngHeader.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Represents the png header chunk. diff --git a/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs b/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs index fdea3eb8a..837a147ed 100644 --- a/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Png/PngImageFormatDetector.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Png +{ /// /// Detects png file headers /// diff --git a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs index b43aff0b7..10ebcc7bb 100644 --- a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs +++ b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png { /// /// Provides enumeration of available PNG interlace modes. diff --git a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs index 5f92cc9e0..6841c1cb8 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Adler32.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Adler32.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Computes Adler32 checksum for a stream of data. An Adler32 /// checksum is not as reliable as a CRC32 checksum, but a lot faster to diff --git a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs index d940112a8..14a29b7af 100644 --- a/src/ImageSharp/Formats/Png/Zlib/Crc32.cs +++ b/src/ImageSharp/Formats/Png/Zlib/Crc32.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; +using System; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. diff --git a/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs b/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs index cbd292dc4..9d84258ca 100644 --- a/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs +++ b/src/ImageSharp/Formats/Png/Zlib/IChecksum.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats +namespace SixLabors.ImageSharp.Formats.Png.Zlib { /// /// Interface to compute a data checksum used by checked input/output streams. diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index c1f04fa98..dd20886ff 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Formats -{ - using System; - using System.IO; - using System.IO.Compression; +using System; +using System.IO; +using System.IO.Compression; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Provides methods and properties for compressing streams by using the Zlib Deflate algorithm. /// diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index 136f919da..36d1d62e7 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -1,11 +1,14 @@ -namespace ImageSharp.Formats -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.IO.Compression; - using System.Text; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +namespace SixLabors.ImageSharp.Formats.Png.Zlib +{ /// /// Provides methods and properties for deframing streams from PNGs. /// diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs index f45582e1e..114eaab2a 100644 --- a/src/ImageSharp/GraphicsOptions.cs +++ b/src/ImageSharp/GraphicsOptions.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Options for influencing the drawing functions. /// diff --git a/src/ImageSharp/Helpers/ImageExtensions.cs b/src/ImageSharp/Helpers/ImageExtensions.cs new file mode 100644 index 000000000..dbf2e34a4 --- /dev/null +++ b/src/ImageSharp/Helpers/ImageExtensions.cs @@ -0,0 +1,61 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Helpers +{ + /// + /// Extension methods over Image{TPixel} + /// + public static partial class ImageExtensions + { + /// + /// Gets the bounds of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Rectangle Bounds(this Image source) + where TPixel : struct, IPixel + => new Rectangle(0, 0, source.Width, source.Height); + + /// + /// Gets the bounds of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Rectangle Bounds(this ImageFrame source) + where TPixel : struct, IPixel + => new Rectangle(0, 0, source.Width, source.Height); + + /// + /// Gets the size of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Size Size(this Image source) + where TPixel : struct, IPixel + => new Size(source.Width, source.Height); + + /// + /// Gets the size of the image. + /// + /// The Pixel format. + /// The source image + /// Returns the bounds of the image + public static Size Size(this ImageFrame source) + where TPixel : struct, IPixel + => new Size(source.Width, source.Height); + } +} diff --git a/src/ImageSharp/ICloningImageProcessor.cs b/src/ImageSharp/ICloningImageProcessor.cs new file mode 100644 index 000000000..aeb3c815e --- /dev/null +++ b/src/ImageSharp/ICloningImageProcessor.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. + /// + /// The pixel format. + internal interface ICloningImageProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Applies the process to the specified portion of the specified . + /// + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// + /// is null. + /// + /// + /// doesnt fit the dimension of the image. + /// + /// Returns the cloned image after thre processor has been applied to it. + Image CloneAndApply(Image source, Rectangle sourceRectangle); + } +} diff --git a/src/ImageSharp/IConfigurationModule.cs b/src/ImageSharp/IConfigurationModule.cs index 95b92ed05..93c40497d 100644 --- a/src/ImageSharp/IConfigurationModule.cs +++ b/src/ImageSharp/IConfigurationModule.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Represents an interface that can register image encoders, decoders and image format detectors. diff --git a/src/ImageSharp/IImageProcessingContextFactory.cs b/src/ImageSharp/IImageProcessingContextFactory.cs new file mode 100644 index 000000000..b7b935ecd --- /dev/null +++ b/src/ImageSharp/IImageProcessingContextFactory.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents an interface that will create IInternalImageProcessingContext instances + /// + internal interface IImageProcessingContextFactory + { + /// + /// Called during mutate operations to generate the image operations provider. + /// + /// The pixel format + /// The source image. + /// A flag to determine whether image operations are allowed to mutate the source image. + /// A new IImageOPeration + IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) + where TPixel : struct, IPixel; + } + + /// + /// The default implmentation of + /// + internal class DefaultImageOperationsProviderFactory : IImageProcessingContextFactory + { + /// + public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) + where TPixel : struct, IPixel + { + return new DefaultInternalImageProcessorContext(source, mutate); + } + } +} diff --git a/src/ImageSharp/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/IImageProcessingContext{TPixel}.cs new file mode 100644 index 000000000..552e8d579 --- /dev/null +++ b/src/ImageSharp/IImageProcessingContext{TPixel}.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// An interface to queue up image operations to apply to an image. + /// + /// The pixel format + public interface IImageProcessingContext + where TPixel : struct, IPixel + { + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply + /// The area to apply it to + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); + + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor); + } + + /// + /// An internal interface to queue up image operations and have a method to apply them to and return a result + /// + /// The pixel format + internal interface IInternalImageProcessingContext : IImageProcessingContext + where TPixel : struct, IPixel + { + /// + /// Adds the processors to the current image + /// + /// The current image or a new image depending on withere this is alloed to mutate the source image. + Image Apply(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageProcessor.cs b/src/ImageSharp/IImageProcessor.cs similarity index 54% rename from src/ImageSharp/Image/IImageProcessor.cs rename to src/ImageSharp/IImageProcessor.cs index 8687766d5..bd6df8d83 100644 --- a/src/ImageSharp/Image/IImageProcessor.cs +++ b/src/ImageSharp/IImageProcessor.cs @@ -1,16 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Encapsulates methods to alter the pixels of an image. /// @@ -19,18 +14,7 @@ namespace ImageSharp.Processing where TPixel : struct, IPixel { /// - /// Gets or sets the parallel options for processing tasks in parallel. - /// - ParallelOptions ParallelOptions { get; set; } - - /// - /// Gets or sets a value indicating whether to compress - /// or expand individual pixel colors the value on processing. - /// - bool Compand { get; set; } - - /// - /// Applies the process to the specified portion of the specified . + /// Applies the process to the specified portion of the specified . /// /// The source image. Cannot be null. /// @@ -42,6 +26,6 @@ namespace ImageSharp.Processing /// /// doesnt fit the dimension of the image. /// - void Apply(ImageBase source, Rectangle sourceRectangle); + void Apply(Image source, Rectangle sourceRectangle); } } diff --git a/src/ImageSharp/IO/BigEndianBitConverter.cs b/src/ImageSharp/IO/BigEndianBitConverter.cs index debe1706c..6b5cf3b9c 100644 --- a/src/ImageSharp/IO/BigEndianBitConverter.cs +++ b/src/ImageSharp/IO/BigEndianBitConverter.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO +namespace SixLabors.ImageSharp.IO { /// /// Implementation of EndianBitConverter which converts to/from big-endian byte arrays. diff --git a/src/ImageSharp/IO/EndianBinaryReader.cs b/src/ImageSharp/IO/EndianBinaryReader.cs index e21d3d3db..0d660c68d 100644 --- a/src/ImageSharp/IO/EndianBinaryReader.cs +++ b/src/ImageSharp/IO/EndianBinaryReader.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.IO; - using System.Text; +using System; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness, depending on the it is constructed with. /// No data is buffered in the reader; the client may seek within the stream at will. diff --git a/src/ImageSharp/IO/EndianBinaryWriter.cs b/src/ImageSharp/IO/EndianBinaryWriter.cs index ef026f00c..dd87faf45 100644 --- a/src/ImageSharp/IO/EndianBinaryWriter.cs +++ b/src/ImageSharp/IO/EndianBinaryWriter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.IO; - using System.Text; +using System; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness, depending on /// the it is constructed with. diff --git a/src/ImageSharp/IO/EndianBitConverter.Conversion.cs b/src/ImageSharp/IO/EndianBitConverter.Conversion.cs index 0858acfed..844c81cc9 100644 --- a/src/ImageSharp/IO/EndianBitConverter.Conversion.cs +++ b/src/ImageSharp/IO/EndianBitConverter.Conversion.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs b/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs index b46a453a4..ea1d7aa5a 100644 --- a/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs +++ b/src/ImageSharp/IO/EndianBitConverter.CopyBytes.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs b/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs index b3e0133e4..5686c829c 100644 --- a/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs +++ b/src/ImageSharp/IO/EndianBitConverter.GetBytes.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.ToType.cs b/src/ImageSharp/IO/EndianBitConverter.ToType.cs index 93b49558a..0c0e49911 100644 --- a/src/ImageSharp/IO/EndianBitConverter.ToType.cs +++ b/src/ImageSharp/IO/EndianBitConverter.ToType.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; +using System; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/EndianBitConverter.cs b/src/ImageSharp/IO/EndianBitConverter.cs index 06b88dbc9..b563a09cb 100644 --- a/src/ImageSharp/IO/EndianBitConverter.cs +++ b/src/ImageSharp/IO/EndianBitConverter.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.IO +{ /// /// Equivalent of , but with either endianness. /// diff --git a/src/ImageSharp/IO/Endianness.cs b/src/ImageSharp/IO/Endianness.cs index aefda6dc4..59b2ae77c 100644 --- a/src/ImageSharp/IO/Endianness.cs +++ b/src/ImageSharp/IO/Endianness.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO +namespace SixLabors.ImageSharp.IO { /// /// Endianness of a converter diff --git a/src/ImageSharp/IO/IFileSystem.cs b/src/ImageSharp/IO/IFileSystem.cs index ee1ef84d7..088d4abb8 100644 --- a/src/ImageSharp/IO/IFileSystem.cs +++ b/src/ImageSharp/IO/IFileSystem.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System.IO; +using System.IO; +namespace SixLabors.ImageSharp.IO +{ #if !NETSTANDARD1_1 /// /// A simple interface representing the filesystem. /// - public interface IFileSystem + internal interface IFileSystem { /// /// Returns a readable stream as defined by the path. diff --git a/src/ImageSharp/IO/LittleEndianBitConverter.cs b/src/ImageSharp/IO/LittleEndianBitConverter.cs index 81ed8d55c..712bfd97b 100644 --- a/src/ImageSharp/IO/LittleEndianBitConverter.cs +++ b/src/ImageSharp/IO/LittleEndianBitConverter.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO +namespace SixLabors.ImageSharp.IO { /// /// Implementation of EndianBitConverter which converts to/from little-endian byte arrays. diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs index 02a9914ea..204f5f4e1 100644 --- a/src/ImageSharp/IO/LocalFileSystem.cs +++ b/src/ImageSharp/IO/LocalFileSystem.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.IO -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.IO +{ #if !NETSTANDARD1_1 /// /// A wrapper around the local File apis. /// - public class LocalFileSystem : IFileSystem + internal class LocalFileSystem : IFileSystem { /// public Stream OpenRead(string path) diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs deleted file mode 100644 index b9e8e392a..000000000 --- a/src/ImageSharp/Image/IImage.cs +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using Formats; - - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - internal interface IImage : IImageBase - { - /// - /// Gets the meta data of the image. - /// - ImageMetaData MetaData { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs deleted file mode 100644 index 0e087aa4a..000000000 --- a/src/ImageSharp/Image/IImageBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using SixLabors.Primitives; - - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - public interface IImageBase - { - /// - /// Gets the representing the bounds of the image. - /// - Rectangle Bounds { get; } - - /// - /// Gets the width in pixels. - /// - int Width { get; } - - /// - /// Gets the height in pixels. - /// - int Height { get; } - - /// - /// Gets the pixel ratio made up of the width and height. - /// - double PixelRatio { get; } - - /// - /// Gets the configuration providing initialization code which allows extending the library. - /// - Configuration Configuration { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase{TPixel}.cs b/src/ImageSharp/Image/IImageBase{TPixel}.cs deleted file mode 100644 index 8b4977b7d..000000000 --- a/src/ImageSharp/Image/IImageBase{TPixel}.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using ImageSharp.PixelFormats; - - /// - /// Encapsulates the basic properties and methods required to manipulate images in varying formats. - /// - /// The pixel format. - public interface IImageBase : IImageBase, IDisposable - where TPixel : struct, IPixel - { - /// - /// Gets the representation of the pixels as an area of contiguous memory in the given pixel format. - /// - Span Pixels { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageFrame.cs b/src/ImageSharp/Image/IImageFrame.cs deleted file mode 100644 index bf3261d93..000000000 --- a/src/ImageSharp/Image/IImageFrame.cs +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - internal interface IImageFrame : IImageBase - { - /// - /// Gets the meta data of the image. - /// - ImageFrameMetaData MetaData { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageFrameCollection.cs b/src/ImageSharp/Image/IImageFrameCollection.cs new file mode 100644 index 000000000..ee325bc63 --- /dev/null +++ b/src/ImageSharp/Image/IImageFrameCollection.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections; +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Encapsulates an imaged collection of frames. + /// + /// The type of the pixel. + public interface IImageFrameCollection : IEnumerable> + where TPixel : struct, IPixel + { + /// + /// Gets the count. + /// + int Count { get; } + + /// + /// Gets the root frame. + /// + ImageFrame RootFrame { get; } + + /// + /// Gets or sets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. + ImageFrame this[int index] { get; set; } + + /// + /// Determines the index of a specific in the . + /// + /// The to locate in the . + /// The index of item if found in the list; otherwise, -1. + int IndexOf(ImageFrame frame); + + /// + /// Inserts the to the at the specified . + /// + /// The zero-based index at which item should be inserted.. + /// The to insert into the . + void Insert(int index, ImageFrame frame); + + /// + /// Removes the from the at the specified index. + /// + /// The zero-based index of the item to remove. + /// Cannot remove last frame. + void RemoveAt(int index); + + /// + /// Adds the specified frame. + /// + /// The frame. + /// Frame must have the same dimensions as the image - frame + void Add(ImageFrame frame); + + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the the specified frame; otherwise, false. + /// + bool Contains(ImageFrame frame); + + /// + /// Removes the specified frame. + /// + /// The frame. + /// true if item is found in the ; otherwise, + /// Cannot remove last frame + bool Remove(ImageFrame frame); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs index 101310706..b4ab712d0 100644 --- a/src/ImageSharp/Image/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - using System.Linq; - using Formats; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the decoding of new images. /// diff --git a/src/ImageSharp/Image/Image.FromBytes.cs b/src/ImageSharp/Image/Image.FromBytes.cs index c7023b860..9da9c5e43 100644 --- a/src/ImageSharp/Image/Image.FromBytes.cs +++ b/src/ImageSharp/Image/Image.FromBytes.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.IO; - using Formats; - - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from a byte array. /// diff --git a/src/ImageSharp/Image/Image.FromFile.cs b/src/ImageSharp/Image/Image.FromFile.cs index ca395ce08..0474a6d47 100644 --- a/src/ImageSharp/Image/Image.FromFile.cs +++ b/src/ImageSharp/Image/Image.FromFile.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ #if !NETSTANDARD1_1 - using System; - using System.IO; - using Formats; - using ImageSharp.PixelFormats; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from a given file. /// @@ -217,5 +215,5 @@ namespace ImageSharp } } } -#endif -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs index 29d93ae85..90fa12e3f 100644 --- a/src/ImageSharp/Image/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using Formats; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from a given stream. /// diff --git a/src/ImageSharp/Image/Image.LoadPixelData.cs b/src/ImageSharp/Image/Image.LoadPixelData.cs index 7b6a4d668..5f1a1617f 100644 --- a/src/ImageSharp/Image/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image/Image.LoadPixelData.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - using Formats; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Adds static methods allowing the creation of new image from raw pixel data. /// @@ -26,7 +25,19 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Span data, int width, int height) + public static Image LoadPixelData(TPixel[] data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(Configuration.Default, data, width, height); + + /// + /// Create a new instance of the class from the raw data. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + private static Image LoadPixelData(Span data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(Configuration.Default, data, width, height); @@ -38,10 +49,35 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Span data, int width, int height) + public static Image LoadPixelData(byte[] data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(Configuration.Default, data, width, height); + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + private static Image LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(Configuration.Default, data, width, height); + + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static Image LoadPixelData(Configuration config, byte[] data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(config, new Span(data).NonPortableCast(), width, height); + /// /// Create a new instance of the class from the given byte array in format. /// @@ -51,7 +87,7 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Configuration config, Span data, int width, int height) + private static Image LoadPixelData(Configuration config, Span data, int width, int height) where TPixel : struct, IPixel => LoadPixelData(config, data.NonPortableCast(), width, height); @@ -64,14 +100,35 @@ namespace ImageSharp /// The height of the final image. /// The pixel format. /// A new . - public static Image LoadPixelData(Configuration config, Span data, int width, int height) + public static Image LoadPixelData(Configuration config, TPixel[] data, int width, int height) + where TPixel : struct, IPixel + { + int count = width * height; + Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); + + var image = new Image(config, width, height); + SpanHelper.Copy(data, image.GetPixelSpan(), count); + + return image; + } + + /// + /// Create a new instance of the class from the raw data. + /// + /// The config for the decoder. + /// The Span containing the image Pixel data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + private static Image LoadPixelData(Configuration config, Span data, int width, int height) where TPixel : struct, IPixel { int count = width * height; Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); var image = new Image(config, width, height); - SpanHelper.Copy(data, image.Pixels, count); + SpanHelper.Copy(data, image.Frames.RootFrame.GetPixelSpan(), count); return image; } diff --git a/src/ImageSharp/Image/ImageBase{TPixel}.cs b/src/ImageSharp/Image/ImageBase{TPixel}.cs deleted file mode 100644 index 20b891f2d..000000000 --- a/src/ImageSharp/Image/ImageBase{TPixel}.cs +++ /dev/null @@ -1,365 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - - /// - /// The base class of all images. Encapsulates the basic properties and methods required to manipulate - /// images in different pixel formats. - /// - /// The pixel format. - [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract class ImageBase : IImageBase - where TPixel : struct, IPixel - { - /// - /// Gets or sets the maximum allowable width in pixels. - /// - public const int MaxWidth = int.MaxValue; - - /// - /// Gets or sets the maximum allowable height in pixels. - /// - public const int MaxHeight = int.MaxValue; - -#pragma warning disable SA1401 // Fields must be private - /// - /// The image pixels. Not private as Buffer2D requires an array in its constructor. - /// - internal TPixel[] PixelBuffer; -#pragma warning restore SA1401 // Fields must be private - - /// - /// A value indicating whether this instance of the given entity has been disposed. - /// - /// if this instance has been disposed; otherwise, . - /// - /// If the entity is disposed, it must not be disposed a second time. The isDisposed field is set the first time the entity - /// is disposed. If the isDisposed field is true, then the Dispose() method will not dispose again. This help not to prolong the entity's - /// life in the Garbage Collector. - /// - private bool isDisposed; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - protected ImageBase(Configuration configuration) - { - this.Configuration = configuration ?? Configuration.Default; - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// The width of the image in pixels. - /// The height of the image in pixels. - /// - /// Thrown if either or are less than or equal to 0. - /// - protected ImageBase(Configuration configuration, int width, int height) - : this(configuration) - { - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - this.Width = width; - this.Height = height; - this.RentPixels(); - this.ClearPixels(); - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The other to create this instance from. - /// - /// - /// Thrown if the given is null. - /// - protected ImageBase(ImageBase other) - : this(other.Configuration) - { - Guard.NotNull(other, nameof(other), "Other image cannot be null."); - - this.Width = other.Width; - this.Height = other.Height; - this.CopyProperties(other); - - // Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. - this.RentPixels(); - using (PixelAccessor sourcePixels = other.Lock()) - using (PixelAccessor target = this.Lock()) - { - // Check we can do this without crashing - sourcePixels.CopyTo(target); - } - } - - /// - public Span Pixels => new Span(this.PixelBuffer, 0, this.Width * this.Height); - - /// - public int Width { get; private set; } - - /// - public int Height { get; private set; } - - /// - public double PixelRatio => (double)this.Width / this.Height; - - /// - public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - - /// - /// Gets the configuration providing initialization code which allows extending the library. - /// - public Configuration Configuration { get; private set; } - - /// - /// Gets or sets the pixel at the specified position. - /// - /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - public TPixel this[int x, int y] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - this.CheckCoordinates(x, y); - return this.PixelBuffer[(y * this.Width) + x]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.CheckCoordinates(x, y); - this.PixelBuffer[(y * this.Width) + x] = value; - } - } - - /// - /// Gets a reference to the pixel at the specified position. - /// - /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TPixel GetPixelReference(int x, int y) - { - this.CheckCoordinates(x, y); - return ref this.PixelBuffer[(y * this.Width) + x]; - } - - /// - /// Gets a representing the row 'y' beginning from the the first pixel on that row. - /// - /// The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the image. - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int y) - { - this.CheckCoordinates(y); - return this.Pixels.Slice(y * this.Width, this.Width); - } - - /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. - /// - /// The x coordinate (position in the row) - /// The y (row) coordinate - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int x, int y) - { - this.CheckCoordinates(x, y); - return this.Pixels.Slice((y * this.Width) + x, this.Width - x); - } - - /// - /// Applies the processor. - /// - /// The processor. - /// The rectangle. - public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - processor.Apply(this, rectangle); - } - - /// - public void Dispose() - { - this.Dispose(true); - - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - - /// - /// Locks the image providing access to the pixels. - /// - /// It is imperative that the accessor is correctly disposed off after use. - /// - /// - /// The - internal PixelAccessor Lock() - { - return new PixelAccessor(this); - } - - /// - /// Copies the pixels to another of the same size. - /// - /// The target pixel buffer accessor. - internal void CopyTo(PixelAccessor target) - { - SpanHelper.Copy(this.Pixels, target.PixelBuffer.Span); - } - - /// - /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer. - /// - /// The pixel source. - internal void SwapPixelsBuffers(PixelAccessor pixelSource) - { - Guard.NotNull(pixelSource, nameof(pixelSource)); - - int newWidth = pixelSource.Width; - int newHeight = pixelSource.Height; - - // Push my memory into the accessor (which in turn unpins the old buffer ready for the images use) - TPixel[] newPixels = pixelSource.ReturnCurrentColorsAndReplaceThemInternally(this.Width, this.Height, this.PixelBuffer); - this.Width = newWidth; - this.Height = newHeight; - this.PixelBuffer = newPixels; - } - - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - protected void CopyProperties(IImageBase other) - { - DebugGuard.NotNull(other, nameof(other)); - - this.Configuration = other.Configuration; - } - - /// - /// Releases any unmanaged resources from the inheriting class. - /// - protected virtual void ReleaseUnmanagedResources() - { - // TODO release unmanaged resources here - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - protected virtual void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - this.ReleaseUnmanagedResources(); - - if (disposing) - { - this.ReturnPixels(); - } - - // Note disposing is done. - this.isDisposed = true; - } - - /// - /// Rents the pixel array from the pool. - /// - private void RentPixels() - { - this.PixelBuffer = PixelDataPool.Rent(this.Width * this.Height); - } - - /// - /// Returns the rented pixel array back to the pool. - /// - private void ReturnPixels() - { - PixelDataPool.Return(this.PixelBuffer); - this.PixelBuffer = null; - } - - /// - /// Clears the pixel array. - /// - private void ClearPixels() - { - Array.Clear(this.PixelBuffer, 0, this.Width * this.Height); - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int y) - { - if (y < 0 || y >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds."); - } - } - - /// - /// Checks the coordinates to ensure they are within bounds. - /// - /// The x-coordinate of the pixel. Must be greater than zero and less than the width of the image. - /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. - /// - /// Thrown if the coordinates are not within the bounds of the image. - /// - [Conditional("DEBUG")] - private void CheckCoordinates(int x, int y) - { - if (x < 0 || x >= this.Width) - { - throw new ArgumentOutOfRangeException(nameof(x), x, $"{x} is outwith the image bounds."); - } - - if (y < 0 || y >= this.Height) - { - throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the image bounds."); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageExtensions.cs b/src/ImageSharp/Image/ImageExtensions.cs new file mode 100644 index 000000000..c5b3d6f31 --- /dev/null +++ b/src/ImageSharp/Image/ImageExtensions.cs @@ -0,0 +1,204 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods over Image{TPixel} + /// + public static partial class ImageExtensions + { +#if !NETSTANDARD1_1 + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The Pixel format. + /// The source image + /// The file path to save the image to. + /// Thrown if the stream is null. + public static void Save(this Image source, string filePath) + where TPixel : struct, IPixel + { + Guard.NotNullOrEmpty(filePath, nameof(filePath)); + + string ext = Path.GetExtension(filePath).Trim('.'); + IImageFormat format = source.GetConfiguration().FindFormatByFileExtension(ext); + if (format == null) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); + foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) + { + stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + + IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + + if (encoder == null) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); + foreach (KeyValuePair enc in source.GetConfiguration().ImageEncoders) + { + stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + + source.Save(filePath, encoder); + } + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The Pixel format. + /// The source image + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the encoder is null. + public static void Save(this Image source, string filePath, IImageEncoder encoder) + where TPixel : struct, IPixel + { + Guard.NotNull(encoder, nameof(encoder)); + using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath)) + { + source.Save(fs, encoder); + } + } +#endif + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The Pixel format. + /// The source image + /// The stream to save the image to. + /// The format to save the image to. + /// Thrown if the stream is null. + public static void Save(this Image source, Stream stream, IImageFormat format) + where TPixel : struct, IPixel + { + Guard.NotNull(format, nameof(format)); + IImageEncoder encoder = source.GetConfiguration().FindEncoder(format); + + if (encoder == null) + { + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); + + foreach (KeyValuePair val in source.GetConfiguration().ImageEncoders) + { + stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); + } + + throw new NotSupportedException(stringBuilder.ToString()); + } + + source.Save(stream, encoder); + } + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// A copy of the pixel data as bytes from this frame. + /// Thrown if the stream is null. + public static byte[] SavePixelData(this ImageFrame source) + where TPixel : struct, IPixel + => source.GetPixelSpan().AsBytes().ToArray(); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this ImageFrame source, byte[] buffer) + where TPixel : struct, IPixel + => SavePixelData(source, new Span(buffer)); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + private static void SavePixelData(this ImageFrame source, Span buffer) + where TPixel : struct, IPixel + { + Span byteBuffer = source.GetPixelSpan().AsBytes(); + Guard.MustBeGreaterThanOrEqualTo(buffer.Length, byteBuffer.Length, nameof(buffer)); + + byteBuffer.CopyTo(buffer); + } + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// A copy of the pixel data from the first frame as bytes. + /// Thrown if the stream is null. + public static byte[] SavePixelData(this Image source) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + public static void SavePixelData(this Image source, byte[] buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer); + + /// + /// Saves the raw image to the given bytes. + /// + /// The Pixel format. + /// The source image + /// The buffer to save the raw pixel data to. + /// Thrown if the stream is null. + private static void SavePixelData(this Image source, Span buffer) + where TPixel : struct, IPixel + => source.Frames.RootFrame.SavePixelData(buffer); + + /// + /// Returns a Base64 encoded string from the given image. + /// + /// + /// The Pixel format. + /// The source image + /// The format. + /// The + public static string ToBase64String(this Image source, IImageFormat format) + where TPixel : struct, IPixel + { + using (var stream = new MemoryStream()) + { + source.Save(stream, format); + stream.Flush(); + return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; + } + } + } +} diff --git a/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs new file mode 100644 index 000000000..aecd9bba9 --- /dev/null +++ b/src/ImageSharp/Image/ImageFrame.LoadPixelData.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Adds static methods allowing the creation of new image from raw pixel data. + /// + public static partial class ImageFrame + { + /// + /// Create a new instance of the class from the given byte array in format. + /// + /// The byte array containing image data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static ImageFrame LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + => LoadPixelData(data.NonPortableCast(), width, height); + + /// + /// Create a new instance of the class from the raw data. + /// + /// The Span containing the image Pixel data. + /// The width of the final image. + /// The height of the final image. + /// The pixel format. + /// A new . + public static ImageFrame LoadPixelData(Span data, int width, int height) + where TPixel : struct, IPixel + { + int count = width * height; + Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); + + var image = new ImageFrame(width, height); + SpanHelper.Copy(data, image.GetPixelSpan(), count); + + return image; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageFrameCollection.cs b/src/ImageSharp/Image/ImageFrameCollection.cs new file mode 100644 index 000000000..25c0d0c44 --- /dev/null +++ b/src/ImageSharp/Image/ImageFrameCollection.cs @@ -0,0 +1,166 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections; +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// Encapsulates an imaged collection of frames. + /// + /// The type of the pixel. + internal sealed class ImageFrameCollection : IImageFrameCollection, IDisposable + where TPixel : struct, IPixel + { + private readonly IList> frames = new List>(); + + internal ImageFrameCollection(int width, int height) + { + this.Add(new ImageFrame(width, height)); + } + + internal ImageFrameCollection(IEnumerable> frames) + { + Guard.NotNullOrEmpty(frames, nameof(frames)); + foreach (ImageFrame f in frames) + { + this.Add(f); + } + } + + /// + /// Gets the count. + /// + public int Count => this.frames.Count; + + /// + /// Gets the root frame. + /// + public ImageFrame RootFrame => this.frames.Count > 0 ? this.frames[0] : null; + + /// + /// Gets or sets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// The at the specified index. + public ImageFrame this[int index] + { + get => this.frames[index]; + + set + { + this.ValidateFrame(value); + this.frames[index] = value; + } + } + + /// + /// Determines the index of a specific in the . + /// + /// The to locate in the . + /// The index of item if found in the list; otherwise, -1. + public int IndexOf(ImageFrame frame) => this.frames.IndexOf(frame); + + /// + /// Inserts the to the at the specified . + /// + /// The zero-based index at which item should be inserted.. + /// The to insert into the . + public void Insert(int index, ImageFrame frame) + { + this.ValidateFrame(frame); + this.frames.Insert(index, frame); + } + + /// + /// Removes the from the at the specified index. + /// + /// The zero-based index of the item to remove. + /// Cannot remove last frame. + public void RemoveAt(int index) + { + if (index == 0 && this.Count == 1) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + this.frames.RemoveAt(index); + } + + /// + /// Adds the specified frame. + /// + /// The frame. + /// Frame must have the same dimensions as the image - frame + public void Add(ImageFrame frame) + { + this.ValidateFrame(frame); + this.frames.Add(frame); + } + + /// + /// Determines whether the contains the . + /// + /// The frame. + /// + /// true if the the specified frame; otherwise, false. + /// + public bool Contains(ImageFrame frame) + { + return this.frames.Contains(frame); + } + + /// + /// Removes the specified frame. + /// + /// The frame. + /// true if item is found in the ; otherwise, + /// Cannot remove last frame + public bool Remove(ImageFrame frame) + { + if (this.Count == 1 && this.frames.Contains(frame)) + { + throw new InvalidOperationException("Cannot remove last frame."); + } + + return this.frames.Remove(frame); + } + + /// + IEnumerator> IEnumerable>.GetEnumerator() => this.frames.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.frames).GetEnumerator(); + + private void ValidateFrame(ImageFrame frame) + { + Guard.NotNull(frame, nameof(frame)); + + if (this.Count != 0) + { + if (this.RootFrame.Width != frame.Width || this.RootFrame.Height != frame.Height) + { + throw new ArgumentException("Frame must have the same dimensions as the image.", nameof(frame)); + } + } + } + + /// + public void Dispose() + { + foreach (ImageFrame f in this.frames) + { + f.Dispose(); + } + + this.frames.Clear(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageFrame{TPixel}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs index 04b9902c6..73e3a80ae 100644 --- a/src/ImageSharp/Image/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -1,78 +1,213 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Represents a single frame in a animation. /// /// The pixel format. - public class ImageFrame : ImageBase, IImageFrame + public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// The image pixels. Not private as Buffer2D requires an array in its constructor. + /// + private Buffer2D pixelBuffer; + + private bool isDisposed; + + /// + /// Initializes a new instance of the class. /// /// The width of the image in pixels. /// The height of the image in pixels. - /// - /// The configuration providing initialization code which allows extending the library. - /// - public ImageFrame(int width, int height, Configuration configuration = null) - : base(configuration, width, height) + internal ImageFrame(int width, int height) + : this(width, height, new ImageFrameMetaData()) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The image to create the frame from. - public ImageFrame(ImageFrame image) - : base(image) + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The meta data. + internal ImageFrame(int width, int height, ImageFrameMetaData metaData) { - this.CopyProperties(image); + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + Guard.NotNull(metaData, nameof(metaData)); + + this.pixelBuffer = Buffer2D.CreateClean(width, height); + this.MetaData = metaData; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The image to create the frame from. - public ImageFrame(ImageBase image) - : base(image) + /// The source. + internal ImageFrame(ImageFrame source) { + this.pixelBuffer = new Buffer2D(source.pixelBuffer.Width, source.pixelBuffer.Height); + source.pixelBuffer.Span.CopyTo(this.pixelBuffer.Span); + this.MetaData = source.MetaData.Clone(); } + /// + Buffer2D IPixelSource.PixelBuffer => this.pixelBuffer; + + /// + /// Gets the width. + /// + public int Width => this.pixelBuffer.Width; + + /// + /// Gets the height. + /// + public int Height => this.pixelBuffer.Height; + /// /// Gets the meta data of the frame. /// - public ImageFrameMetaData MetaData { get; private set; } = new ImageFrameMetaData(); + public ImageFrameMetaData MetaData { get; private set; } + + /// + /// Gets or sets the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + public TPixel this[int x, int y] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.pixelBuffer[x, y]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.pixelBuffer[x, y] = value; + } + } + + /// + /// Performs an explicit conversion from to . + /// + /// The image. + /// + /// The result of the conversion. + /// + public static implicit operator ImageFrame(Image image) => image.Frames[0]; + + /// + /// Gets a reference to the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref TPixel GetPixelReference(int x, int y) + { + return ref this.pixelBuffer[x, y]; + } + + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The + internal PixelAccessor Lock() + { + return new PixelAccessor(this); + } + + /// + /// Copies the pixels to another of the same size. + /// + /// The target pixel buffer accessor. + internal void CopyTo(PixelAccessor target) + { + SpanHelper.Copy(this.GetPixelSpan(), target.PixelBuffer.Span); + } + + /// + /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer. + /// + /// The pixel source. + internal void SwapPixelsBuffers(PixelAccessor pixelSource) + { + Guard.NotNull(pixelSource, nameof(pixelSource)); + + // Push my memory into the accessor (which in turn unpins the old buffer ready for the images use) + Buffer2D newPixels = pixelSource.SwapBufferOwnership(this.pixelBuffer); + this.pixelBuffer = newPixels; + } + + /// + /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer. + /// + /// The pixel source. + internal void SwapPixelsBuffers(ImageFrame pixelSource) + { + Guard.NotNull(pixelSource, nameof(pixelSource)); + + ComparableExtensions.Swap(ref this.pixelBuffer, ref pixelSource.pixelBuffer); + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + this.pixelBuffer?.Dispose(); + this.pixelBuffer = null; + + // Note disposing is done. + this.isDisposed = true; + } /// public override string ToString() { - return $"ImageFrame: {this.Width}x{this.Height}"; + return $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; } /// /// Returns a copy of the image frame in the given pixel format. /// - /// A function that allows for the correction of vector scaling between unknown color formats. /// The pixel format. /// The - public ImageFrame To(Func scaleFunc = null) + public ImageFrame CloneAs() where TPixel2 : struct, IPixel { - scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); + if (typeof(TPixel2) == typeof(TPixel)) + { + return this.Clone() as ImageFrame; + } + + Func scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(); - ImageFrame target = new ImageFrame(this.Width, this.Height, this.Configuration); - target.CopyProperties(this); + var target = new ImageFrame(this.Width, this.Height, this.MetaData.Clone()); using (PixelAccessor pixels = this.Lock()) using (PixelAccessor targetPixels = target.Lock()) @@ -80,12 +215,12 @@ namespace ImageSharp Parallel.For( 0, target.Height, - this.Configuration.ParallelOptions, + Configuration.Default.ParallelOptions, y => { for (int x = 0; x < target.Width; x++) { - TPixel2 color = default(TPixel2); + var color = default(TPixel2); color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); targetPixels[x, y] = color; } @@ -99,22 +234,9 @@ namespace ImageSharp /// Clones the current instance. /// /// The - internal virtual ImageFrame Clone() + public ImageFrame Clone() { return new ImageFrame(this); } - - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - private void CopyProperties(IImageFrame other) - { - base.CopyProperties(other); - - this.MetaData = new ImageFrameMetaData(other.MetaData); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs deleted file mode 100644 index 8eed103d1..000000000 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Applies the processor to the image. - /// This method does not resize the target image. - /// - /// The pixel format. - /// The image this method extends. - /// The processor to apply to the image. - /// The . - public static Image Apply(this Image source, IImageProcessor processor) - where TPixel : struct, IPixel - { - source.ApplyProcessor(processor, source.Bounds); - return source; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 5e8bcab31..5c35d854a 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -1,31 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp { - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Numerics; - using System.Text; - using System.Threading.Tasks; - - using Formats; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - [DebuggerDisplay("Image: {Width}x{Height}")] - public class Image : ImageBase, IImage + public sealed partial class Image : IDisposable, IConfigurable where TPixel : struct, IPixel { + private Configuration configuration; + private ImageFrameCollection frames; + /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -51,37 +48,6 @@ namespace ImageSharp { } - /// - /// Initializes a new instance of the class - /// by making a copy from another image. - /// - /// The other image, where the clone should be made from. - /// is null. - public Image(Image other) - : base(other) - { - foreach (ImageFrame frame in other.Frames) - { - if (frame != null) - { - this.Frames.Add(new ImageFrame(frame)); - } - } - - this.CopyProperties(other); - } - - /// - /// Initializes a new instance of the class - /// by making a copy from another image. - /// - /// The other image, where the clone should be made from. - /// is null. - public Image(ImageBase other) - : base(other) - { - } - /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -93,87 +59,68 @@ namespace ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) - : base(configuration, width, height) { + this.configuration = configuration ?? Configuration.Default; this.MetaData = metadata ?? new ImageMetaData(); + this.frames = new ImageFrameCollection(width, height); } /// - /// Gets the meta data of the image. + /// Initializes a new instance of the class + /// with the height and the width of the image. /// - public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); + /// The configuration providing initialization code which allows extending the library. + /// The images metadata. + /// The frames that will be owned by this image instance. + internal Image(Configuration configuration, ImageMetaData metadata, IEnumerable> frames) + { + this.configuration = configuration ?? Configuration.Default; + this.MetaData = metadata ?? new ImageMetaData(); + + this.frames = new ImageFrameCollection(frames); + } /// - /// Gets the width of the image in inches. It is calculated as the width of the image - /// in pixels multiplied with the density. When the density is equals or less than zero - /// the default value is used. + /// Gets the pixel buffer. /// - /// The width of the image in inches. - public double InchWidth => this.Width / this.MetaData.HorizontalResolution; + Configuration IConfigurable.Configuration => this.configuration; /// - /// Gets the height of the image in inches. It is calculated as the height of the image - /// in pixels multiplied with the density. When the density is equals or less than zero - /// the default value is used. + /// Gets the width. /// - /// The height of the image in inches. - public double InchHeight => this.Height / this.MetaData.VerticalResolution; + public int Width => this.frames.RootFrame.Width; /// - /// Gets a value indicating whether this image is animated. + /// Gets the height. /// - /// - /// True if this image is animated; otherwise, false. - /// - public bool IsAnimated => this.Frames.Count > 0; + public int Height => this.frames.RootFrame.Height; /// - /// Gets the other frames for the animation. + /// Gets the meta data of the image. /// - /// The list of frame images. - public IList> Frames { get; } = new List>(); + public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); /// - /// Applies the processor to the image. + /// Gets the frames. /// - /// The processor to apply to the image. - /// The structure that specifies the portion of the image object to draw. - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - // we want to put this on on here as it gives us a really go place to test/verify processor settings - base.ApplyProcessor(processor, rectangle); - foreach (ImageFrame sourceFrame in this.Frames) - { - sourceFrame.ApplyProcessor(processor, rectangle); - } - } + public IImageFrameCollection Frames => this.frames; /// - /// Saves the image to the given stream using the currently loaded image format. + /// Gets the root frame. /// - /// The stream to save the image to. - /// The format to save the image to. - /// Thrown if the stream is null. - /// The - public Image Save(Stream stream, IImageFormat format) - { - Guard.NotNull(format, nameof(format)); - IImageEncoder encoder = this.Configuration.FindEncoder(format); - - if (encoder == null) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:"); - - foreach (KeyValuePair val in this.Configuration.ImageEncoders) - { - stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); - } + private IPixelSource PixelSource => this.frames?.RootFrame ?? throw new ObjectDisposedException(nameof(Image)); - throw new NotSupportedException(stringBuilder.ToString()); - } + /// + /// Gets or sets the pixel at the specified position. + /// + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. + /// The at the specified position. + public TPixel this[int x, int y] + { + get => this.PixelSource.PixelBuffer[x, y]; - return this.Save(stream, encoder); + set => this.PixelSource.PixelBuffer[x, y] = value; } /// @@ -182,170 +129,65 @@ namespace ImageSharp /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream or encoder is null. - /// - /// The . - /// - public Image Save(Stream stream, IImageEncoder encoder) + public void Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); encoder.Encode(this, stream); - - return this; } -#if !NETSTANDARD1_1 /// - /// Saves the image to the given stream using the currently loaded image format. + /// Clones the current image /// - /// The file path to save the image to. - /// Thrown if the stream is null. - /// The - public Image Save(string filePath) + /// Returns a new image with all the same metadata as the original. + public Image Clone() { - Guard.NotNullOrEmpty(filePath, nameof(filePath)); + IEnumerable> frames = this.frames.Select(x => x.Clone()).ToArray(); - string ext = Path.GetExtension(filePath).Trim('.'); - var format = this.Configuration.FindFormatByFileExtensions(ext); - if (format == null) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); - foreach (IImageFormat fmt in this.Configuration.ImageFormats) - { - stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); - } - - throw new NotSupportedException(stringBuilder.ToString()); - } - - IImageEncoder encoder = this.Configuration.FindEncoder(format); - - if (encoder == null) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); - foreach (KeyValuePair enc in this.Configuration.ImageEncoders) - { - stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); - } - - throw new NotSupportedException(stringBuilder.ToString()); - } - - return this.Save(filePath, encoder); + return new Image(this.configuration, this.MetaData.Clone(), frames); } - /// - /// Saves the image to the given stream using the currently loaded image format. - /// - /// The file path to save the image to. - /// The encoder to save the image with. - /// Thrown if the encoder is null. - /// The - public Image Save(string filePath, IImageEncoder encoder) - { - Guard.NotNull(encoder, nameof(encoder)); - using (Stream fs = this.Configuration.FileSystem.Create(filePath)) - { - return this.Save(fs, encoder); - } - } -#endif - /// public override string ToString() { return $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; } - /// - /// Returns a Base64 encoded string from the given image. - /// - /// - /// The format. - /// The - public string ToBase64String(IImageFormat format) - { - using (var stream = new MemoryStream()) - { - this.Save(stream, format); - stream.Flush(); - return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}"; - } - } - /// /// Returns a copy of the image in the given pixel format. /// - /// A function that allows for the correction of vector scaling between unknown color formats. /// The pixel format. /// The - public Image To(Func scaleFunc = null) + public Image CloneAs() where TPixel2 : struct, IPixel { - scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); - - var target = new Image(this.Configuration, this.Width, this.Height); - target.CopyProperties(this); - - using (PixelAccessor pixels = this.Lock()) - using (PixelAccessor targetPixels = target.Lock()) - { - Parallel.For( - 0, - target.Height, - this.Configuration.ParallelOptions, - y => - { - for (int x = 0; x < target.Width; x++) - { - var color = default(TPixel2); - color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); - targetPixels[x, y] = color; - } - }); - } - - for (int i = 0; i < this.Frames.Count; i++) - { - target.Frames.Add(this.Frames[i].To()); - } + IEnumerable> frames = this.frames.Select(x => x.CloneAs()).ToArray(); + var target = new Image(this.configuration, this.MetaData, frames); return target; } /// - /// Creates a new from this instance + /// Releases managed resources. /// - /// The - internal virtual ImageFrame ToFrame() + public void Dispose() { - return new ImageFrame(this); - } - - /// - protected override void Dispose(bool disposing) - { - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < this.Frames.Count; i++) - { - this.Frames[i].Dispose(); - } - - base.Dispose(disposing); + this.frames.Dispose(); } /// - /// Copies the properties from the other . + /// Switches the buffers used by the image and the pixelSource meaning that the Image will "own" the buffer from the pixelSource and the pixelSource will now own the Images buffer. /// - /// - /// The other to copy the properties from. - /// - private void CopyProperties(IImage other) + /// The pixel source. + internal void SwapPixelsBuffers(Image pixelSource) { - this.MetaData = new ImageMetaData(other.MetaData); + Guard.NotNull(pixelSource, nameof(pixelSource)); + + for (int i = 0; i < this.frames.Count; i++) + { + this.frames[i].SwapPixelsBuffers(pixelSource.frames[i]); + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessorExtensions.cs b/src/ImageSharp/Image/PixelAccessorExtensions.cs new file mode 100644 index 000000000..9146ef48d --- /dev/null +++ b/src/ImageSharp/Image/PixelAccessorExtensions.cs @@ -0,0 +1,54 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Unsafe = System.Runtime.CompilerServices.Unsafe; + +namespace SixLabors.ImageSharp +{ + /// + /// Helper methods fro acccess pixel accessors + /// + internal static class PixelAccessorExtensions + { + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The type of the pixel. + /// The frame. + /// + /// The + /// + internal static PixelAccessor Lock(this IPixelSource frame) + where TPixel : struct, IPixel + { + return new PixelAccessor(frame); + } + + /// + /// Locks the image providing access to the pixels. + /// + /// It is imperative that the accessor is correctly disposed off after use. + /// + /// + /// The type of the pixel. + /// The image. + /// + /// The + /// + internal static PixelAccessor Lock(this Image image) + where TPixel : struct, IPixel + { + return image.Frames.RootFrame.Lock(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 3902ba425..3abe28aca 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Unsafe = System.Runtime.CompilerServices.Unsafe; +namespace SixLabors.ImageSharp +{ /// /// Provides per-pixel access to generic pixels. /// @@ -25,6 +23,7 @@ namespace ImageSharp /// The containing the pixel data. /// internal Buffer2D PixelBuffer; + private bool ownedBuffer; #pragma warning restore SA1401 // Fields must be private /// @@ -42,14 +41,13 @@ namespace ImageSharp /// Initializes a new instance of the class. /// /// The image to provide pixel access for. - public PixelAccessor(ImageBase image) + public PixelAccessor(IPixelSource image) { Guard.NotNull(image, nameof(image)); - Guard.MustBeGreaterThan(image.Width, 0, "image width"); - Guard.MustBeGreaterThan(image.Height, 0, "image height"); + Guard.MustBeGreaterThan(image.PixelBuffer.Width, 0, "image width"); + Guard.MustBeGreaterThan(image.PixelBuffer.Height, 0, "image height"); - this.SetPixelBufferUnsafe(image.Width, image.Height, image.PixelBuffer); - this.ParallelOptions = image.Configuration.ParallelOptions; + this.SetPixelBufferUnsafe(image.PixelBuffer, false); } /// @@ -58,7 +56,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, Buffer2D.CreateClean(width, height)) + : this(width, height, Buffer2D.CreateClean(width, height), true) { } @@ -68,15 +66,14 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - private PixelAccessor(int width, int height, Buffer2D pixels) + /// if set to true [owned buffer]. + private PixelAccessor(int width, int height, Buffer2D pixels, bool ownedBuffer) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - this.SetPixelBufferUnsafe(width, height, pixels); - - this.ParallelOptions = Configuration.Default.ParallelOptions; + this.SetPixelBufferUnsafe(pixels, ownedBuffer); } /// @@ -102,21 +99,12 @@ namespace ImageSharp /// public int RowStride { get; private set; } - /// - /// Gets the width of the image. - /// + /// public int Width { get; private set; } - /// - /// Gets the height of the image. - /// + /// public int Height { get; private set; } - /// - /// Gets the global parallel options for processing tasks in parallel. - /// - public ParallelOptions ParallelOptions { get; } - /// Span IBuffer2D.Span => this.PixelBuffer; @@ -145,12 +133,10 @@ namespace ImageSharp } } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// public void Dispose() { - if (this.isDisposed) + if (this.isDisposed || !this.ownedBuffer) { return; } @@ -238,15 +224,13 @@ namespace ImageSharp /// /// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!! /// - /// The width. - /// The height. /// The pixels. /// Returns the old pixel data thats has gust been replaced. /// If is true then caller is responsible for ensuring is called. - internal TPixel[] ReturnCurrentColorsAndReplaceThemInternally(int width, int height, TPixel[] pixels) + internal Buffer2D SwapBufferOwnership(Buffer2D pixels) { - TPixel[] oldPixels = this.PixelBuffer.TakeArrayOwnership(); - this.SetPixelBufferUnsafe(width, height, pixels); + Buffer2D oldPixels = this.PixelBuffer; + this.SetPixelBufferUnsafe(pixels, this.ownedBuffer); return oldPixels; } @@ -414,23 +398,18 @@ namespace ImageSharp } } - private void SetPixelBufferUnsafe(int width, int height, TPixel[] pixels) - { - this.SetPixelBufferUnsafe(width, height, new Buffer2D(pixels, width, height)); - } - /// /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// - /// The width. - /// The height. /// The pixel buffer - private void SetPixelBufferUnsafe(int width, int height, Buffer2D pixels) + /// if set to true then this instance ownes the buffer and thus should dispose of it afterwards. + private void SetPixelBufferUnsafe(Buffer2D pixels, bool ownedBuffer) { this.PixelBuffer = pixels; + this.ownedBuffer = ownedBuffer; - this.Width = width; - this.Height = height; + this.Width = pixels.Width; + this.Height = pixels.Height; this.PixelSize = Unsafe.SizeOf(); this.RowStride = this.Width * this.PixelSize; } diff --git a/src/ImageSharp/Image/PixelArea{TPixel}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs index 4ddfcadb7..e9924f823 100644 --- a/src/ImageSharp/Image/PixelArea{TPixel}.cs +++ b/src/ImageSharp/Image/PixelArea{TPixel}.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.IO; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics; +using System.IO; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Represents an area of generic pixels. /// diff --git a/src/ImageSharp/ImageFormats.cs b/src/ImageSharp/ImageFormats.cs index f79191eae..bc437e5a5 100644 --- a/src/ImageSharp/ImageFormats.cs +++ b/src/ImageSharp/ImageFormats.cs @@ -1,12 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +namespace SixLabors.ImageSharp +{ /// /// The static collection of all the default image formats /// @@ -30,6 +32,6 @@ namespace ImageSharp /// /// The format details for the bitmaps. /// - public static readonly IImageFormat Bitmap = new BmpFormat(); + public static readonly IImageFormat Bmp = new BmpFormat(); } } \ No newline at end of file diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 44734ddec..146393e53 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -1,20 +1,21 @@  A cross-platform library for the processing of image files; written in C# - ImageSharp - 1.0.0-alpha9 - James Jackson-South and contributors + SixLabors.ImageSharp + $(packageversion) + 0.0.1 + Six Labors and contributors netstandard1.3;netstandard1.1 true true - ImageSharp - ImageSharp + SixLabors.ImageSharp + SixLabors.ImageSharp Image Resize Crop Gif Jpg Jpeg Bitmap Png Core - https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-128.png - https://github.com/JimBobSquarePants/ImageSharp + https://raw.githubusercontent.com/SixLabor/ImageSharp/master/build/icons/imagesharp-logo-128.png + https://github.com/SixLabor/ImageSharp http://www.apache.org/licenses/LICENSE-2.0 git - https://github.com/JimBobSquarePants/ImageSharp + https://github.com/SixLabor/ImageSharp false false false @@ -31,10 +32,10 @@ - - + + All @@ -48,11 +49,16 @@ ..\..\ImageSharp.ruleset + SixLabors.ImageSharp true + + TextTemplatingFileGenerator + Block8x8F.Generated.cs + TextTemplatingFileGenerator Block8x8F.Generated.cs @@ -78,6 +84,11 @@ + + True + True + Block8x8F.Generated.tt + True True diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 046bfd81f..401003eed 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Memory +{ /// /// Defines extension methods for . /// @@ -41,5 +40,55 @@ namespace ImageSharp.Memory { return buffer.Span.Slice(y * buffer.Width, buffer.Width); } + + /// + /// Returns the size of the buffer. + /// + /// The element type + /// The + /// The of the buffer + public static Size Size(this IBuffer2D buffer) + where T : struct + { + return new Size(buffer.Width, buffer.Height); + } + + /// + /// Returns a representing the full area of the buffer. + /// + /// The element type + /// The + /// The + public static Rectangle FullRectangle(this IBuffer2D buffer) + where T : struct + { + return new Rectangle(0, 0, buffer.Width, buffer.Height); + } + + /// + /// Return a to the subarea represented by 'rectangle' + /// + /// The element type + /// The + /// The rectangel subarea + /// The + public static BufferArea GetArea(this IBuffer2D buffer, Rectangle rectangle) + where T : struct => new BufferArea(buffer, rectangle); + + public static BufferArea GetArea(this IBuffer2D buffer, int x, int y, int width, int height) + where T : struct + { + var rectangle = new Rectangle(x, y, width, height); + return new BufferArea(buffer, rectangle); + } + + /// + /// Return a to the whole area of 'buffer' + /// + /// The element type + /// The + /// The + public static BufferArea GetArea(this IBuffer2D buffer) + where T : struct => new BufferArea(buffer); } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer2D.cs b/src/ImageSharp/Memory/Buffer2D{T}.cs similarity index 76% rename from src/ImageSharp/Memory/Buffer2D.cs rename to src/ImageSharp/Memory/Buffer2D{T}.cs index 59cabb1bd..99b10cae7 100644 --- a/src/ImageSharp/Memory/Buffer2D.cs +++ b/src/ImageSharp/Memory/Buffer2D{T}.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Memory +{ /// /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. @@ -15,6 +14,11 @@ namespace ImageSharp.Memory internal class Buffer2D : Buffer, IBuffer2D where T : struct { + public Buffer2D(Size size) + : this(size.Width, size.Height) + { + } + /// /// Initializes a new instance of the class. /// @@ -57,6 +61,9 @@ namespace ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] get { + DebugGuard.MustBeLessThan(x, this.Width, nameof(x)); + DebugGuard.MustBeLessThan(y, this.Height, nameof(y)); + return ref this.Array[(this.Width * y) + x]; } } @@ -73,5 +80,12 @@ namespace ImageSharp.Memory buffer.Clear(); return buffer; } + + /// + /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// + /// The size of the buffer + /// The instance + public static Buffer2D CreateClean(Size size) => CreateClean(size.Width, size.Height); } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs new file mode 100644 index 000000000..b5ed3566f --- /dev/null +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.CompilerServices; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Represents a rectangular area inside a 2D memory buffer (). + /// This type is kind-of 2D Span, but it can live on heap. + /// + /// The element type + internal struct BufferArea + where T : struct + { + /// + /// The rectangle specifying the boundaries of the area in . + /// + public readonly Rectangle Rectangle; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea(IBuffer2D destinationBuffer, Rectangle rectangle) + { + DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle)); + DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, destinationBuffer.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, destinationBuffer.Height, nameof(rectangle)); + + this.DestinationBuffer = destinationBuffer; + this.Rectangle = rectangle; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea(IBuffer2D destinationBuffer) + : this(destinationBuffer, destinationBuffer.FullRectangle()) + { + } + + /// + /// Gets the being pointed by this instance. + /// + public IBuffer2D DestinationBuffer { get; } + + /// + /// Gets the size of the area. + /// + public Size Size => this.Rectangle.Size; + + /// + /// Gets the pixel stride which is equal to the width of . + /// + public int Stride => this.DestinationBuffer.Width; + + /// + /// Gets or sets a value at the given index. + /// + /// The position inside a row + /// The row index + /// The reference to the value + public ref T this[int x, int y] => ref this.DestinationBuffer.Span[this.GetIndexOf(x, y)]; + + /// + /// Gets a reference to the [0,0] element. + /// + /// The reference to the [0,0] element + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T GetReferenceToOrigo() => + ref this.DestinationBuffer.Span[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; + + /// + /// Gets a span to row 'y' inside this area. + /// + /// The row index + /// The span + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int y) + { + int yy = this.GetRowIndex(y); + int xx = this.Rectangle.X; + int width = this.Rectangle.Width; + + return this.DestinationBuffer.Span.Slice(yy + xx, width); + } + + /// + /// Returns a sub-area as . (Similar to .) + /// + /// The x index at the subarea origo + /// The y index at the subarea origo + /// The desired width of the subarea + /// The desired height of the subarea + /// The subarea + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea GetSubArea(int x, int y, int width, int height) + { + var rectangle = new Rectangle(x, y, width, height); + return this.GetSubArea(rectangle); + } + + /// + /// Returns a sub-area as . (Similar to .) + /// + /// The specifying the boundaries of the subarea + /// The subarea + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferArea GetSubArea(Rectangle rectangle) + { + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle)); + DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle)); + + int x = this.Rectangle.X + rectangle.X; + int y = this.Rectangle.Y + rectangle.Y; + rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height); + return new BufferArea(this.DestinationBuffer, rectangle); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetIndexOf(int x, int y) + { + int yy = this.GetRowIndex(y); + int xx = this.Rectangle.X + x; + return yy + xx; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int GetRowIndex(int y) + { + return (y + this.Rectangle.Y) * this.DestinationBuffer.Width; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/Buffer.cs b/src/ImageSharp/Memory/Buffer{T}.cs similarity index 94% rename from src/ImageSharp/Memory/Buffer.cs rename to src/ImageSharp/Memory/Buffer{T}.cs index 4b3681c74..f5c9ed00a 100644 --- a/src/ImageSharp/Memory/Buffer.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -1,20 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.Memory +{ + /// /// /// Manages a buffer of value type objects as a Disposable resource. /// The backing array is either pooled or comes from the outside. /// /// The value type. - internal class Buffer : IDisposable + internal class Buffer : IBuffer where T : struct { /// @@ -207,7 +206,8 @@ namespace ImageSharp.Memory { if (this.IsDisposedOrLostArrayOwnership) { - throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); + throw new InvalidOperationException( + "TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); } this.IsDisposedOrLostArrayOwnership = true; diff --git a/src/ImageSharp/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs index 260c829e2..14ac58baf 100644 --- a/src/ImageSharp/Memory/Fast2DArray{T}.cs +++ b/src/ImageSharp/Memory/Fast2DArray{T}.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.Memory +{ /// /// Provides fast access to 2D arrays. /// @@ -106,11 +104,23 @@ namespace ImageSharp.Memory return new Fast2DArray(data); } + /// + /// Gets a representing the row beginning from the the first item on that row. + /// + /// The y-coordinate of the row. Must be greater than or equal to zero and less than the height of the 2D array. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetRowSpan(int row) + { + this.CheckCoordinates(row); + return new Span(this.Data, row * this.Width, this.Width); + } + /// /// Checks the coordinates to ensure they are within bounds. /// - /// The row-coordinate of the item. Must be greater than zero and smaller than the height of the array. - /// The column-coordinate of the item. Must be greater than zero and smaller than the width of the array. + /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the array. + /// The x-coordinate of the item. Must be greater than zero and smaller than the width of the array. /// /// Thrown if the coordinates are not within the bounds of the array. /// @@ -127,5 +137,21 @@ namespace ImageSharp.Memory throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the array bounds."); } } + + /// + /// Checks the coordinates to ensure they are within bounds. + /// + /// The y-coordinate of the item. Must be greater than zero and smaller than the height of the array. + /// + /// Thrown if the coordinates are not within the bounds of the image. + /// + [Conditional("DEBUG")] + private void CheckCoordinates(int row) + { + if (row < 0 || row >= this.Height) + { + throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds."); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/IBuffer2D.cs b/src/ImageSharp/Memory/IBuffer2D{T}.cs similarity index 79% rename from src/ImageSharp/Memory/IBuffer2D.cs rename to src/ImageSharp/Memory/IBuffer2D{T}.cs index 300c29a1b..2f60fd02a 100644 --- a/src/ImageSharp/Memory/IBuffer2D.cs +++ b/src/ImageSharp/Memory/IBuffer2D{T}.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; +using System; +namespace SixLabors.ImageSharp.Memory +{ /// /// An interface that represents a pinned buffer of value type objects /// interpreted as a 2D region of x elements. diff --git a/src/ImageSharp/Memory/IBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer{T}.cs new file mode 100644 index 000000000..a0f80063f --- /dev/null +++ b/src/ImageSharp/Memory/IBuffer{T}.cs @@ -0,0 +1,18 @@ +using System; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// + /// Represents a contigous memory buffer of value-type items "promising" a + /// + /// The value type + internal interface IBuffer : IDisposable + where T : struct + { + /// + /// Gets the span to the memory "promised" by this buffer + /// + Span Span { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Memory/PixelDataPool{T}.cs index 643f1c6ca..6f4cef707 100644 --- a/src/ImageSharp/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Memory/PixelDataPool{T}.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System.Buffers; - - using ImageSharp.PixelFormats; +using System.Buffers; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Memory +{ /// /// Provides a resource pool that enables reusing instances of value type arrays for image data . /// diff --git a/src/ImageSharp/Memory/SpanHelper.cs b/src/ImageSharp/Memory/SpanHelper.cs index 0e794e1b5..8b9fad522 100644 --- a/src/ImageSharp/Memory/SpanHelper.cs +++ b/src/ImageSharp/Memory/SpanHelper.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Memory -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using Unsafe = System.Runtime.CompilerServices.Unsafe; +namespace SixLabors.ImageSharp.Memory +{ /// /// Utility methods for /// diff --git a/src/ImageSharp/MetaData/IMetaData.cs b/src/ImageSharp/MetaData/IFrameMetaData.cs similarity index 80% rename from src/ImageSharp/MetaData/IMetaData.cs rename to src/ImageSharp/MetaData/IFrameMetaData.cs index 6daa04dd6..168b7802c 100644 --- a/src/ImageSharp/MetaData/IMetaData.cs +++ b/src/ImageSharp/MetaData/IFrameMetaData.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Encapsulates the metadata of an image frame. /// - internal interface IMetaData + internal interface IFrameMetaData { /// /// Gets or sets the frame delay for animated images. diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs index b55bfd1ad..e5c2afa87 100644 --- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs +++ b/src/ImageSharp/MetaData/ImageFrameMetaData.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Encapsulates the metadata of an image frame. /// - public sealed class ImageFrameMetaData : IMetaData + public sealed class ImageFrameMetaData : IFrameMetaData { /// /// Initializes a new instance of the class. @@ -39,5 +38,14 @@ namespace ImageSharp /// public DisposalMethod DisposalMethod { get; set; } + + /// + /// Clones this ImageFrameMetaData. + /// + /// The cloned instance. + public ImageFrameMetaData Clone() + { + return new ImageFrameMetaData(this); + } } } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index 5361b486d..72af56945 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Collections.Generic; - using ImageSharp.Formats; +using System.Collections.Generic; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Encapsulates the metadata of an image. /// - public sealed class ImageMetaData : IMetaData + public sealed class ImageMetaData : IFrameMetaData { /// /// The default horizontal resolution value (dots per inch) in x direction. @@ -132,6 +133,15 @@ namespace ImageSharp /// public ushort RepeatCount { get; set; } + /// + /// Clones this into a new instance + /// + /// The cloned metadata instance + public ImageMetaData Clone() + { + return new ImageMetaData(this); + } + /// /// Synchronizes the profiles with the current meta data. /// diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index 178794283..62ae9d479 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/src/ImageSharp/MetaData/ImageProperty.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData +{ /// /// Stores meta information about a image, like the name of the author, /// the copyright information, the date, where the image was created diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs index f2d012c6c..8c3c1171c 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { /// /// Specifies exif data types. diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs index 880a43453..b1b42ad43 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Specifies which parts will be written when the profile is added to an image. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index a7fd8fd6a..f72753cc2 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.IO; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents an EXIF profile providing access to the collection of values. /// @@ -82,9 +80,11 @@ namespace ImageSharp this.values.Add(new ExifValue(value)); } } - else + + if (other.data != null) { - this.data = other.data; + this.data = new byte[other.data.Length]; + Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length); } } @@ -131,7 +131,7 @@ namespace ImageSharp return null; } - if (this.data.Length < (this.thumbnailOffset + this.thumbnailLength)) + if (this.data == null || this.data.Length < (this.thumbnailOffset + this.thumbnailLength)) { return null; } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 53123bfc2..e247527a6 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Linq; - using System.Text; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Reads and parses EXIF data from a byte array /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs index 6f4b49485..5d2ef1436 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { /// /// All exif tags from the Exif standard 2.2 diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs index e08bddd3f..4021227f5 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Class that provides a description for an ExifTag value. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs index a2965917b..64508137b 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; - using System.Text; +using System; +using System.Globalization; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represent the value of the EXIF profile. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs index bf8f4caa2..7f4123225 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Text; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Contains methods for writing EXIF metadata. /// diff --git a/src/ImageSharp/Numerics/LongRational.cs b/src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs similarity index 97% rename from src/ImageSharp/Numerics/LongRational.cs rename to src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs index f56abc289..f9c16d57d 100644 --- a/src/ImageSharp/Numerics/LongRational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/LongRational.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; - using System.Text; +using System; +using System.Globalization; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents a number that can be expressed as a fraction /// diff --git a/src/ImageSharp/Numerics/Rational.cs b/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs similarity index 97% rename from src/ImageSharp/Numerics/Rational.cs rename to src/ImageSharp/MetaData/Profiles/Exif/Rational.cs index d219a5469..6d62a623f 100644 --- a/src/ImageSharp/Numerics/Rational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/Rational.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents a number that can be expressed as a fraction. /// diff --git a/src/ImageSharp/Numerics/SignedRational.cs b/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs similarity index 97% rename from src/ImageSharp/Numerics/SignedRational.cs rename to src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs index bd2213dce..f2fe35924 100644 --- a/src/ImageSharp/Numerics/SignedRational.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/SignedRational.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Exif +{ /// /// Represents a number that can be expressed as a fraction. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs index f34f006e7..157453f1b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccCurveSegment.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A segment of a curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs index ad03c8ff8..5d931039c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccFormulaCurveElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A formula based curve segment /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs index 3f6497ccc..d916486db 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccOneDimensionalCurve.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A one dimensional curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs index 14aaf153e..46aec49be 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A parametric curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs index a5816e212..ae9cd84b4 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A response curve /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs index 859d43338..572c4a8f7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Curves/IccSampledCurveElement.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A sampled curve segment /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs index ba32586fe..0ea404ad9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs index a34ee42fc..f9d56b0ff 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Lut.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs index 23ad9feb2..495d73a78 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Matrix.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs index 371c5c42a..2b39b1d6c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElement.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to read ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs index 44a892084..8b942498a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs index 85d80c7a8..14f7f9570 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Text; +using System; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index 0fe03284f..5c14448fa 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; - using System.Numerics; +using System; +using System.Globalization; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs index cb1fe5b55..1fecd761a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Text; - using ImageSharp.IO; +using System; +using System.Text; +using SixLabors.ImageSharp.IO; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to read ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs index 4b6e454a1..d5f5d5a9b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Curves.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs index f85d59714..38a3dd457 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Lut.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs index 13a15b483..0b7064cf9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Numerics; - - using ImageSharp.Memory; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs index b3ddb538c..48c6734ae 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElement.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Provides methods to write ICC data types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs index d90c1ff54..791a94a33 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs index fbb7065e6..bf39a0849 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Text; +using System; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index b74f22054..9ee4f0004 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Linq; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs index a9a65b80b..cfcc66c8e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.IO; - using System.Text; +using System; +using System.IO; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Provides methods to write ICC data types /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs index 066cbe848..fe0c9b8b5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccClutDataType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Color lookup table data type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs index 3f57ded74..0913cda41 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorSpaceType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Color Space Type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs index 251a848b7..bec71e0db 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccColorantEncoding.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Colorant Encoding diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs index 14515c113..cfd2671e9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveMeasurementEncodings.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Curve Measurement Encodings diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs index 77ded0d1b..e9b8cc26b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccCurveSegmentSignature.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Curve Segment Signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs index a4ca4befa..b13edb53f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDataType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the basic data types as defined in ICC.1:2010 version 4.3.0.0 diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs index c57cf4f97..88cca866d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccDeviceAttribute.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Device attributes. Can be combined with a logical OR /// The least-significant 32 bits are defined by the ICC, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs index eacc1eb28..2a375b0d1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccFormulaCurveType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Formula curve segment type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs index fdfb78049..fe6575c41 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMeasurementGeometry.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Measurement Geometry diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs index 8ab690b64..fe5d30922 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccMultiProcessElementSignature.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Multi process element signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs index 823b41340..374b4ad93 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccParametricCurveType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Formula curve segment type diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs index 8fdeb3f41..1563078f7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccPrimaryPlatformType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the primary platform/operating system framework for which the profile was created diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs index 9fb0b51f3..71c27ca61 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Profile Class Name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs index aa8df88bd..3758be34b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Profile flags. Can be combined with a logical OR. /// The least-significant 16 bits are reserved for the ICC, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs index 6eab5b3fe..82b296900 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs @@ -1,10 +1,8 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// // ReSharper disable InconsistentNaming -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the ICC Profile Tags as defined in ICC.1:2010 version 4.3.0.0 diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs index 10e0fbac9..e0504b24c 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Rendering intent diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs index 2938d4469..839ab8c6b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningFlag.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Screening flags. Can be combined with a logical OR. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs index 0d24c3ae5..f1d73c626 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccScreeningSpotType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Enumerates the screening spot types diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs index 5fc2fa228..4773ab6c2 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccSignatureName.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Signature Name diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs index 3526887ed..fe0c40657 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardIlluminant.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Standard Illuminant diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs index 0efc5fd40..a393c258b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccStandardObserver.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Standard Observer diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs index a42cc8cd1..1493ecc6b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// Type Signature diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs b/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs index 54fe7c764..d867edbeb 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Exceptions/InvalidIccProfileException.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Represents an error that happened while reading or writing a corrupt/invalid ICC profile /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs index 978d5bc24..df85b2ab8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; + #if !NETSTANDARD1_1 - using System.Security.Cryptography; +using System.Security.Cryptography; #endif +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Represents an ICC profile /// @@ -59,7 +58,11 @@ namespace ImageSharp Guard.NotNull(other, nameof(other)); // TODO: Do we need to copy anything else? - this.data = other.data; + if (other.data != null) + { + this.data = new byte[other.data.Length]; + Buffer.BlockCopy(other.data, 0, this.data, 0, other.data.Length); + } } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs index 8237dc7a8..f91572cfe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfileHeader.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Contains all values of an ICC profile header /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs index b24c96f02..ea84113fd 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Reads and parses ICC data from a byte array /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs index 5db3d96ea..4789a69fe 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The data of an ICC tag entry /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index 19c00e8f5..d7221e8cf 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Contains methods for writing ICC profiles. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs index a20a52d8e..09931a1f9 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccBAcsProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A placeholder (might be used for future ICC versions) /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs index 7d5855168..384ae850f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccClutProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A CLUT (color lookup table) element to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs index c16ca93d2..d04869548 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccCurveSetProcessElement.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A set of curves to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs index 9219f5200..7e0e1eda8 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccEAcsProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A placeholder (might be used for future ICC versions) /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs index 259f71489..642f766d5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMatrixProcessElement.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - - using ImageSharp.Memory; +using System; +using System.Linq; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A matrix element to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs index 205e61f01..59acd0eb7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/MultiProcessElements/IccMultiProcessElement.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// An element to process data /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs index 85f40f5e4..6d838558a 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccChromaticityTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The chromaticity tag type provides basic chromaticity data /// and type of phosphors or colorants of a monitor to applications and utilities. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs index 9be5541a2..56af95a16 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantOrderTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This tag specifies the laydown order in which colorants /// will be printed on an n-colorant device. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs index 041f74f39..b04ee10d0 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccColorantTableTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The purpose of this tag is to identify the colorants used in /// the profile by a unique name and set of PCSXYZ or PCSLAB values diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs index 73588bbf0..1e516ce7f 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCrdInfoTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type contains the PostScript product name to which this profile /// corresponds and the names of the companion CRDs diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs index c14995748..7f753ff7d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The type contains a one-dimensional table of double values. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs index 57f04b11a..3b17e2942 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Text; +using System; +using System.Linq; +using System.Text; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The dataType is a simple data structure that contains /// either 7-bit ASCII or binary data, i.e. textType data or transparent bytes. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs index 7111913cb..879c208c1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDateTimeTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type is a representation of the time and date. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs index c91347d23..23580fac6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccFix16ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of doubles (from 32bit fixed point values). /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs index 45d7b3c50..13e146330 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut16TagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform using tables /// with 16-bit precision. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs index 538cf0505..fd6d028ea 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLut8TagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform using tables /// with 8-bit precision. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs index d440447cc..faf9a4550 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutAToBTagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs index 9d63dd171..10ffca335 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccLutBToATagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs index 390a5ba54..12010ea09 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMeasurementTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The measurementType information refers only to the internal /// profile data and is meant to provide profile makers an alternative diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs index 573e77ed4..62792b44e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiLocalizedUnicodeTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This tag structure contains a set of records each referencing /// a multilingual string associated with a profile. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs index e3e6ef143..437c6734b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccMultiProcessElementsTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This structure represents a color transform, containing /// a sequence of processing elements. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs index 17b7213b2..d3e73b018 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccNamedColor2TagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The namedColor2Type is a count value and array of structures /// that provide color coordinates for color names. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs index 4264c0b24..192be52a1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccParametricCurveTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The parametricCurveType describes a one-dimensional curve by /// specifying one of a predefined set of functions using the parameters. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs index 547483e23..88aaa0976 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceDescTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type is an array of structures, each of which contains information /// from the header fields and tags from the original profiles which were diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs index 1a1b02c0b..7ef17d37e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccProfileSequenceIdentifierTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type is an array of structures, each of which contains information /// for identification of a profile used in a sequence. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs index f4fc7fbd2..7faad30f3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccResponseCurveSet16TagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The purpose of this tag type is to provide a mechanism to relate physical /// colorant amounts with the normalized device codes produced by lut8Type, lut16Type, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs index 8c3a2d83b..7a5062707 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccScreeningTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type describes various screening parameters including /// screen frequency, screening angle, and spot shape. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs index 9ce2c1b9a..82462afa3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccSignatureTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Typically this type is used for registered tags that can /// be displayed on many development systems as a sequence of four characters. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs index 202220f93..71030c2a6 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextDescriptionTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The TextDescriptionType contains three types of text description. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs index 9e56a1cf8..8127fae29 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccTextTagDataEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This is a simple text structure that contains a text string. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs index 2b7b4044d..709106a52 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUFix16ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of doubles (from 32bit values). /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs index da7725310..bcd991cea 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt16ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of unsigned shorts. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs index 0dabdecc1..8a5ccb0c0 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt32ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of unsigned 32bit integers. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs index d62a4a8a7..6e22f7758 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt64ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of unsigned 64bit integers. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs index 30363df9d..07e142d49 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUInt8ArrayTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents an array of bytes. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs index e668e5cfd..0f8bd6c33 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUcrBgTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type contains curves representing the under color removal and black generation /// and a text string which is a general description of the method used for the UCR and BG. diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs index 79549b7c8..8b60aad26 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccUnknownTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This tag stores data of an unknown tag data entry /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs index 5f7406ef1..2c12066f1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccViewingConditionsTagDataEntry.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// This type represents a set of viewing condition parameters. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs index 77f9ff706..d704fee96 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccXyzTagDataEntry.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; - using System.Numerics; +using System; +using System.Linq; +using System.Numerics; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// The XYZType contains an array of XYZ values. /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs index d527d4052..af7e3e011 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccClut.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Color Lookup Table /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs index bd8955075..17127110d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccColorantTableEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Entry of ICC colorant table /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs index 0e2772963..68a5e7cfa 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLocalizedString.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A string with a specific locale /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs index 9b86e1113..12d3208a7 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccLut.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Lookup Table /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs index 5d5161568..98107e828 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccNamedColor.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A specific color with a name /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs index 768aead0e..6258ca2f3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccPositionNumber.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Position of an object within an ICC profile /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs index 4f744631d..455717a03 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileDescription.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// ICC Profile description /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs index fc4760d3f..1f96540df 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileId.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// ICC Profile ID /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs index 65507bf6b..dfa3fb203 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccProfileSequenceIdentifier.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Linq; +using System; +using System.Linq; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Description of a profile within a sequence /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs index 5c58aa1b1..a10c55f4e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccResponseNumber.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Associates a normalized device code with a measurement value /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs index ac0e89bb0..f41858f30 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccScreeningChannel.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// A single channel of a /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs index 793756999..5464de9c5 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Various/IccTagTableEntry.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; +using System; +namespace SixLabors.ImageSharp.MetaData.Profiles.Icc +{ /// /// Entry of ICC tag table /// diff --git a/src/ImageSharp/Numerics/ValueSize.cs b/src/ImageSharp/Numerics/ValueSize.cs new file mode 100644 index 000000000..659e0ebfe --- /dev/null +++ b/src/ImageSharp/Numerics/ValueSize.cs @@ -0,0 +1,129 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Represents a value in relation to a value on the image + /// + internal struct ValueSize : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// The value. + /// The type. + public ValueSize(float value, ValueSizeType type) + { + if (type != ValueSizeType.Absolute) + { + Guard.MustBeBetweenOrEqualTo(value, 0, 1, nameof(value)); + } + + this.Value = value; + this.Type = type; + } + + /// + /// The different vlaue types + /// + public enum ValueSizeType + { + /// + /// The value is the final return value + /// + Absolute, + + /// + /// The value is a percentage of the Images Width + /// + PercentageOfWidth, + + /// + /// The value is a percentage of the Images height + /// + PercentageOfHeight + } + + /// + /// Gets the value. + /// + public float Value { get; } + + /// + /// Gets the type. + /// + public ValueSizeType Type { get; } + + /// + /// Implicitly converts a float into an absolute value + /// + /// the vlaue to use as the absolute figure. + public static implicit operator ValueSize(float d) + => Absolute(d); + + /// + /// Create a new ValueSize with as a PercentageOfWidth type with value set to percentage. + /// + /// The percentage. + /// a Values size with type PercentageOfWidth + public static ValueSize PercentageOfWidth(float percentage) + { + return new ValueSize(percentage, ValueSizeType.PercentageOfWidth); + } + + /// + /// Create a new ValueSize with as a PercentageOfHeight type with value set to percentage. + /// + /// The percentage. + /// a Values size with type PercentageOfHeight + public static ValueSize PercentageOfHeight(float percentage) + { + return new ValueSize(percentage, ValueSizeType.PercentageOfHeight); + } + + /// + /// Create a new ValueSize with as a Absolute type with value set to value. + /// + /// The value. + /// a Values size with type Absolute( + public static ValueSize Absolute(float value) + { + return new ValueSize(value, ValueSizeType.Absolute); + } + + /// + /// Calculates the specified size. + /// + /// The size. + /// The calucalted value + public float Calculate(Size size) + { + switch (this.Type) + { + case ValueSizeType.PercentageOfWidth: + return this.Value * size.Width; + case ValueSizeType.PercentageOfHeight: + return this.Value * size.Height; + case ValueSizeType.Absolute: + default: + return this.Value; + } + } + + /// + public override string ToString() + { + return $"{this.Value} - {this.Type}"; + } + + /// + public bool Equals(ValueSize other) + { + return this.Type == other.Type && this.Value == other.Value; + } + } +} diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs index 59934fdc6..c266035a6 100644 --- a/src/ImageSharp/PixelFormats/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/Alpha8.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing a single 8 bit normalized W values. /// diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/Argb32.cs index f389723dc..33294838e 100644 --- a/src/ImageSharp/PixelFormats/Argb32.cs +++ b/src/ImageSharp/PixelFormats/Argb32.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in alpha, red, green, and blue order. diff --git a/src/ImageSharp/PixelFormats/Bgr24.cs b/src/ImageSharp/PixelFormats/Bgr24.cs index aaed5c385..e210856b3 100644 --- a/src/ImageSharp/PixelFormats/Bgr24.cs +++ b/src/ImageSharp/PixelFormats/Bgr24.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in blue, green, red order. diff --git a/src/ImageSharp/PixelFormats/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs index af22b14a0..b4e7aad58 100644 --- a/src/ImageSharp/PixelFormats/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/Bgr565.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. /// diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/Bgra32.cs index f1ac20b56..e8469414d 100644 --- a/src/ImageSharp/PixelFormats/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/Bgra32.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in blue, green, red, and alpha order. diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs index 746e1062b..c51a872d1 100644 --- a/src/ImageSharp/PixelFormats/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/Bgra4444.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. /// diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs index 198f91108..8be4ce82c 100644 --- a/src/ImageSharp/PixelFormats/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/Bgra5551.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. /// diff --git a/src/ImageSharp/PixelFormats/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs index 14053ba12..829937c8a 100644 --- a/src/ImageSharp/PixelFormats/Byte4.cs +++ b/src/ImageSharp/PixelFormats/Byte4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. /// diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index 92fb006ab..c646d804a 100644 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Globalization; +using System; +using System.Globalization; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// A set of named colors mapped to the provided Color space. /// diff --git a/src/ImageSharp/PixelFormats/ColorConstants.cs b/src/ImageSharp/PixelFormats/ColorConstants.cs index 236d3a889..f97d3b190 100644 --- a/src/ImageSharp/PixelFormats/ColorConstants.cs +++ b/src/ImageSharp/PixelFormats/ColorConstants.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Provides useful color definitions. /// diff --git a/src/ImageSharp/PixelFormats/ComponentOrder.cs b/src/ImageSharp/PixelFormats/ComponentOrder.cs index 8f151b41d..868d08259 100644 --- a/src/ImageSharp/PixelFormats/ComponentOrder.cs +++ b/src/ImageSharp/PixelFormats/ComponentOrder.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +namespace SixLabors.ImageSharp.PixelFormats { /// /// Enumerates the various component orders. /// - public enum ComponentOrder + internal enum ComponentOrder { /// /// Z-> Y-> X order. Equivalent to B-> G-> R in diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs index c042d7678..9553ec82d 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -1,14 +1,10 @@ -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +// +namespace SixLabors.ImageSharp.PixelFormats { using System; - using System.Numerics; using System.Runtime.CompilerServices; public partial class PixelOperations diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt index 16292489f..aa88b6606 100644 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -95,17 +93,13 @@ } #> -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +// +namespace SixLabors.ImageSharp.PixelFormats { using System; - using System.Numerics; using System.Runtime.CompilerServices; public partial class PixelOperations diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs index e42c575d8..659e70228 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -1,19 +1,13 @@ -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +// +namespace SixLabors.ImageSharp { using System; - using System.Numerics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; /// /// Provides optimized overrides for bulk operations. @@ -38,7 +32,6 @@ namespace ImageSharp Unsafe.As(ref dp) = sp; dp.A = 255; } } - /// internal override void ToRgb24(Span sourcePixels, Span dest, int count) @@ -55,7 +48,6 @@ namespace ImageSharp dp = Unsafe.As(ref sp); } } - /// internal override void PackFromBgr24(Span source, Span destPixels, int count) @@ -72,7 +64,6 @@ namespace ImageSharp dp.Bgr = sp; dp.A = 255; } } - /// internal override void ToBgr24(Span sourcePixels, Span dest, int count) @@ -89,7 +80,6 @@ namespace ImageSharp dp = sp.Bgr; } } - /// internal override void PackFromBgra32(Span source, Span destPixels, int count) @@ -106,7 +96,6 @@ namespace ImageSharp dp = sp.ToRgba32(); } } - /// internal override void ToBgra32(Span sourcePixels, Span dest, int count) @@ -123,7 +112,6 @@ namespace ImageSharp dp = sp.ToBgra32(); } } - } } diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt index 9c01fa915..9d2229394 100644 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -11,7 +9,6 @@ <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> <# - void GeneratePackFromMethod(string pixelType, string converterCode) { #> @@ -31,7 +28,6 @@ <#=converterCode#> } } - <# } @@ -54,27 +50,19 @@ <#=converterCode#> } } - <# } - #> -// - -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +// +namespace SixLabors.ImageSharp { using System; - using System.Numerics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; /// /// Provides optimized overrides for bulk operations. diff --git a/src/ImageSharp/PixelFormats/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs index 3bdfc9f1c..b4bc491eb 100644 --- a/src/ImageSharp/PixelFormats/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/HalfSingle.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing a single 16 bit floating point value. /// @@ -180,7 +178,7 @@ namespace ImageSharp.PixelFormats [MethodImpl(MethodImplOptions.AggressiveInlining)] private Vector4 ToScaledVector4() { - Vector4 vector = this.ToVector4(); + var vector = this.ToVector4(); vector *= MaxBytes; vector += Half; vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); diff --git a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs index 740795adc..4d6ef0fb4 100644 --- a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs +++ b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Helper methods for packing and unpacking floating point values /// diff --git a/src/ImageSharp/PixelFormats/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs index 7f1fe4ebd..aa5f32190 100644 --- a/src/ImageSharp/PixelFormats/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/HalfVector2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit floating-point values. /// diff --git a/src/ImageSharp/PixelFormats/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs index 062287dbe..87a1c9a49 100644 --- a/src/ImageSharp/PixelFormats/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/HalfVector4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit floating-point values. /// diff --git a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs index ec283e6f2..6775cbc58 100644 --- a/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs +++ b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; +using System; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// This interface exists for ensuring signature compatibility to MonoGame and XNA packed color types. /// diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 9090e1210..37c505c84 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// An interface that represents a generic pixel type. /// diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index 0b55dcbf9..45050de72 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +namespace SixLabors.ImageSharp.PixelFormats { /// /// A set of named colors mapped to the provided color space. diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs index 992986f92..9a69f6ab3 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs index 99f603f69..920f92cae 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/NormalizedShort2.cs index a0615563f..6d28f61c2 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs index f35fb6368..45f984da0 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. /// diff --git a/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs index 29ef5675e..0537ff514 100644 --- a/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs +++ b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; +using System; +using System.Numerics; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Assists with the conversion of known packed pixel formats from one to another. /// @@ -21,18 +19,11 @@ namespace ImageSharp.PixelFormats /// /// Returns the correct scaling function for the given types The compute scale function. /// - /// The scale function. /// The source pixel format. /// The target pixel format. /// The - public static Func ComputeScaleFunction(Func scaleFunc) + public static Func ComputeScaleFunction() { - // Custom type with a custom function. - if (scaleFunc != null) - { - return scaleFunc; - } - Type source = typeof(TPixel); Type target = typeof(TPixel2); diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs index 1e48f7181..ae70cdee4 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Collections.Generic; - using System.Text; +using System; +using System.Collections.Generic; +using System.Text; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// The various blending modes. /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 915d9a924..99a20516d 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -1,14 +1,12 @@ -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders +// +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { using System; using System.Numerics; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index 230b05499..9d7d73db9 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -10,17 +8,15 @@ <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders +// +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { using System; using System.Numerics; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; /// diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 4213be0ba..efdd275b4 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -1,15 +1,12 @@ -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders +// +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { using System.Numerics; using System.Runtime.CompilerServices; - internal static partial class PorterDuffFunctions { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 53d22d8f3..e6a2ca3eb 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -1,8 +1,6 @@ <# -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// #> <#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> @@ -10,18 +8,15 @@ <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> -// -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders +// +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { using System.Numerics; using System.Runtime.CompilerServices; - internal static partial class PorterDuffFunctions { <# diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs index b1fca9520..04c750255 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats.PixelBlenders -{ - using System.Numerics; - using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ /// /// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model. /// diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 1a1d1cd05..54cb09c28 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; +using System; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Abstract base class for calling pixel composition functions /// diff --git a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs b/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs index 1ea262895..37b225d2e 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs @@ -1,8 +1,11 @@ -namespace ImageSharp.PixelFormats -{ - using System; - using System.Runtime.CompilerServices; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Extension methods for copying single pixel data into byte Spans. /// TODO: This utility class exists for legacy reasons. Need to do a lot of chore work to remove it (mostly in test classes). diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs index 5a3737dc6..154ec7373 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Provides access to pixel blenders /// diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index a62d14527..4f879fbdc 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations /// for pixel buffers of type . diff --git a/src/ImageSharp/PixelFormats/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs index 0575689a7..d2c296515 100644 --- a/src/ImageSharp/PixelFormats/Rg32.cs +++ b/src/ImageSharp/PixelFormats/Rg32.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. /// diff --git a/src/ImageSharp/PixelFormats/Rgb24.cs b/src/ImageSharp/PixelFormats/Rgb24.cs index b8cc8dc24..5a12cff20 100644 --- a/src/ImageSharp/PixelFormats/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/Rgb24.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in red, green, blue order. diff --git a/src/ImageSharp/PixelFormats/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs index e682aa477..e6967d23e 100644 --- a/src/ImageSharp/PixelFormats/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/Rgba1010102.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed vector type containing unsigned normalized values ranging from 0 to 1. /// The x, y and z components use 10 bits, and the w component uses 2 bits. diff --git a/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs b/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs index ab4c2ea60..b5fed5a35 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Provides standardized deifinitions for named colors. /// diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index 63e40e9cf..6f4f93d87 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -1,18 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Provides optimized overrides for bulk operations. /// @@ -53,10 +50,10 @@ namespace ImageSharp nameof(count), "Argument 'count' should divisible by Vector.Count!"); - Vector bVec = new Vector(256.0f / 255.0f); - Vector magicFloat = new Vector(32768.0f); - Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f - Vector mask = new Vector(255); + var bVec = new Vector(256.0f / 255.0f); + var magicFloat = new Vector(32768.0f); + var magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f + var mask = new Vector(255); int unpackedRawCount = count * 4; @@ -83,7 +80,7 @@ namespace ImageSharp vi &= mask; vi |= magicInt; - Vector vf = Vector.AsVectorSingle(vi); + var vf = Vector.AsVectorSingle(vi); vf = (vf - magicFloat) * bVec; Unsafe.Add(ref destBaseAsFloat, i) = vf; @@ -104,7 +101,6 @@ namespace ImageSharp } int remainder = count % Vector.Count; - int alignedCount = count - remainder; if (alignedCount > 0) @@ -120,6 +116,35 @@ namespace ImageSharp } } + internal override void PackFromVector4(Span sourceVectors, Span destColors, int count) + { + GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count); + + if (!SimdUtils.IsAvx2) + { + base.PackFromVector4(sourceVectors, destColors, count); + return; + } + + int remainder = count % 2; + int alignedCount = count - remainder; + + if (alignedCount > 0) + { + Span flatSrc = sourceVectors.Slice(0, alignedCount).NonPortableCast(); + Span flatDest = destColors.NonPortableCast(); + + SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(flatSrc, flatDest); + } + + if (remainder > 0) + { + // actually: remainder == 1 + int lastIdx = count - 1; + destColors[lastIdx].PackFromVector4(sourceVectors[lastIdx]); + } + } + /// internal override void PackFromRgba32(Span source, Span destPixels, int count) { diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs index 85322c7c5..51647fc1f 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using ImageSharp.PixelFormats; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp +{ /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in red, green, blue, and alpha order. @@ -432,7 +429,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(float x, float y, float z, float w) { - Vector4 value = new Vector4(x, y, z, w); + var value = new Vector4(x, y, z, w); this.Pack(ref value); } @@ -443,7 +440,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Pack(ref Vector3 vector) { - Vector4 value = new Vector4(vector, 1); + var value = new Vector4(vector, 1); this.Pack(ref value); } diff --git a/src/ImageSharp/PixelFormats/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs index bdcf13763..040ae15b1 100644 --- a/src/ImageSharp/PixelFormats/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/Rgba64.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1. /// diff --git a/src/ImageSharp/PixelFormats/RgbaComponent.cs b/src/ImageSharp/PixelFormats/RgbaComponent.cs index ed85fb86b..76df62c43 100644 --- a/src/ImageSharp/PixelFormats/RgbaComponent.cs +++ b/src/ImageSharp/PixelFormats/RgbaComponent.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Enumerates the RGBA (red, green, blue, alpha) color components. /// - public enum RgbaComponent + internal enum RgbaComponent { /// /// The red component. diff --git a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs index dc965f9ff..2ef37c43a 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats +namespace SixLabors.ImageSharp.PixelFormats { /// /// Provides operators and composition algorithms. diff --git a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs index 00b746166..1886df29f 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Provides optimized overrides for bulk operations. /// diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs index c6eed1122..ba641d590 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. /// The color components are stored in red, green, blue, and alpha order. diff --git a/src/ImageSharp/PixelFormats/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs index 0b3f4be93..1355a9413 100644 --- a/src/ImageSharp/PixelFormats/Short2.cs +++ b/src/ImageSharp/PixelFormats/Short2.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing two 16-bit signed integer values. /// diff --git a/src/ImageSharp/PixelFormats/Short4.cs b/src/ImageSharp/PixelFormats/Short4.cs index 958300929..aecb4d2de 100644 --- a/src/ImageSharp/PixelFormats/Short4.cs +++ b/src/ImageSharp/PixelFormats/Short4.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.PixelFormats -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +namespace SixLabors.ImageSharp.PixelFormats +{ /// /// Packed pixel type containing four 16-bit signed integer values. /// diff --git a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs index e37f80c25..5a165659b 100644 --- a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs +++ b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,11 @@ namespace ImageSharp /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The . - public static Image BinaryThreshold(this Image source, float threshold) + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) where TPixel : struct, IPixel { - return BinaryThreshold(source, threshold, source.Bounds); + source.ApplyProcessor(new BinaryThresholdProcessor(threshold)); + return source; } /// @@ -40,7 +37,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle) + public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); diff --git a/src/ImageSharp/Processing/Binarization/Dither.cs b/src/ImageSharp/Processing/Binarization/Dither.cs index efaf63b06..f21ccf0bd 100644 --- a/src/ImageSharp/Processing/Binarization/Dither.cs +++ b/src/ImageSharp/Processing/Binarization/Dither.cs @@ -1,22 +1,33 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; - using ImageSharp.Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// The . + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new OrderedDitherProcessor(dither, 0)); + return source; + } + /// /// Dithers the image reducing it to two colors using ordered dithering. /// @@ -25,10 +36,28 @@ namespace ImageSharp /// The ordered ditherer. /// The component index to test the threshold against. Must range from 0 to 3. /// The . - public static Image Dither(this Image source, IOrderedDither dither, int index = 0) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, int index) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new OrderedDitherProcessor(dither, index)); + return source; + } + + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel { - return Dither(source, dither, source.Bounds, index); + source.ApplyProcessor(new OrderedDitherProcessor(dither, 0), rectangle); + return source; } /// @@ -42,7 +71,7 @@ namespace ImageSharp /// /// The component index to test the threshold against. Must range from 0 to 3. /// The . - public static Image Dither(this Image source, IOrderedDither dither, Rectangle rectangle, int index = 0) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle, int index) where TPixel : struct, IPixel { source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); @@ -57,10 +86,11 @@ namespace ImageSharp /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The . - public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) where TPixel : struct, IPixel { - return Dither(source, diffuser, threshold, source.Bounds); + source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold)); + return source; } /// @@ -74,7 +104,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) + public static IImageProcessingContext Dither(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs index 6a4e3807b..300c07381 100644 --- a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs +++ b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,11 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image BlackWhite(this Image source) + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) where TPixel : struct, IPixel { - return BlackWhite(source, source.Bounds); + source.ApplyProcessor(new BlackWhiteProcessor()); + return source; } /// @@ -39,7 +36,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BlackWhite(this Image source, Rectangle rectangle) + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs index 14641afba..ebfa9ffdc 100644 --- a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs +++ b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,10 +21,11 @@ namespace ImageSharp /// The image this method extends. /// The type of color blindness simulator to apply. /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness) + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindness colorBlindness) where TPixel : struct, IPixel { - return ColorBlindness(source, colorBlindness, source.Bounds); + source.ApplyProcessor(GetProcessor(colorBlindness)); + return source; } /// @@ -41,48 +38,35 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle) + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindness colorBlindness, Rectangle rectangle) where TPixel : struct, IPixel { - IImageProcessor processor; + source.ApplyProcessor(GetProcessor(colorBlindness), rectangle); + return source; + } + private static IImageProcessor GetProcessor(ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { switch (colorBlindness) { case ImageSharp.Processing.ColorBlindness.Achromatomaly: - processor = new AchromatomalyProcessor(); - break; - + return new AchromatomalyProcessor(); case ImageSharp.Processing.ColorBlindness.Achromatopsia: - processor = new AchromatopsiaProcessor(); - break; - + return new AchromatopsiaProcessor(); case ImageSharp.Processing.ColorBlindness.Deuteranomaly: - processor = new DeuteranomalyProcessor(); - break; - + return new DeuteranomalyProcessor(); case ImageSharp.Processing.ColorBlindness.Deuteranopia: - processor = new DeuteranopiaProcessor(); - break; - + return new DeuteranopiaProcessor(); case ImageSharp.Processing.ColorBlindness.Protanomaly: - processor = new ProtanomalyProcessor(); - break; - + return new ProtanomalyProcessor(); case ImageSharp.Processing.ColorBlindness.Protanopia: - processor = new ProtanopiaProcessor(); - break; - + return new ProtanopiaProcessor(); case ImageSharp.Processing.ColorBlindness.Tritanomaly: - processor = new TritanomalyProcessor(); - break; - + return new TritanomalyProcessor(); default: - processor = new TritanopiaProcessor(); - break; + return new TritanopiaProcessor(); } - - source.ApplyProcessor(processor, rectangle); - return source; } } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs index 635b6747a..bcf48d3e2 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -22,7 +19,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Grayscale(this Image source) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source) where TPixel : struct, IPixel { return Grayscale(source, GrayscaleMode.Bt709); @@ -35,10 +32,15 @@ namespace ImageSharp /// The image this method extends. /// The formula to apply to perform the operation. /// The . - public static Image Grayscale(this Image source, GrayscaleMode mode) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) where TPixel : struct, IPixel { - return Grayscale(source, mode, source.Bounds); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor() + : new GrayscaleBt601Processor(); + + source.ApplyProcessor(processor); + return source; } /// @@ -50,13 +52,10 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Grayscale(this Image source, Rectangle rectangle) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { - IImageProcessor processor = new GrayscaleBt709Processor(); - - source.ApplyProcessor(processor, rectangle); - return source; + return Grayscale(source, GrayscaleMode.Bt709, rectangle); } /// @@ -69,7 +68,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Grayscale(this Image source, GrayscaleMode mode, Rectangle rectangle) + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) where TPixel : struct, IPixel { IImageProcessor processor = mode == GrayscaleMode.Bt709 diff --git a/src/ImageSharp/Processing/ColorMatrix/Hue.cs b/src/ImageSharp/Processing/ColorMatrix/Hue.cs index d218b3a10..bfc931977 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Hue.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Hue.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,10 +21,11 @@ namespace ImageSharp /// The image this method extends. /// The angle in degrees to adjust the image. /// The . - public static Image Hue(this Image source, float degrees) + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) where TPixel : struct, IPixel { - return Hue(source, degrees, source.Bounds); + source.ApplyProcessor(new HueProcessor(degrees)); + return source; } /// @@ -41,7 +38,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Hue(this Image source, float degrees, Rectangle rectangle) + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new HueProcessor(degrees), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs index 09eb13190..d7845320a 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,11 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Kodachrome(this Image source) + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) where TPixel : struct, IPixel { - return Kodachrome(source, source.Bounds); + source.ApplyProcessor(new KodachromeProcessor()); + return source; } /// @@ -39,7 +36,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Kodachrome(this Image source, Rectangle rectangle) + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new KodachromeProcessor(), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs index bca4577e4..947e53157 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,10 +20,10 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Lomograph(this Image source) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source) where TPixel : struct, IPixel { - return Lomograph(source, source.Bounds, GraphicsOptions.Default); + return Lomograph(source, GraphicsOptions.Default); } /// @@ -39,7 +35,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Lomograph(this Image source, Rectangle rectangle) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return Lomograph(source, rectangle, GraphicsOptions.Default); @@ -52,10 +48,11 @@ namespace ImageSharp /// The image this method extends. /// The options effecting pixel blending. /// The . - public static Image Lomograph(this Image source, GraphicsOptions options) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - return Lomograph(source, source.Bounds, options); + source.ApplyProcessor(new LomographProcessor(options)); + return source; } /// @@ -68,7 +65,7 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Lomograph(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { source.ApplyProcessor(new LomographProcessor(options), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs b/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs index def253234..1b92029f6 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Options/ColorBlindness.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the various types of defined color blindness filters. diff --git a/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs b/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs index f1294ffaf..370071b7a 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Options/GrayscaleMode.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the various types of defined Grayscale filters. diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs index b2d8515d6..c96087d57 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Polaroid(this Image source) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source) where TPixel : struct, IPixel { return Polaroid(source, GraphicsOptions.Default); @@ -39,7 +35,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Polaroid(this Image source, Rectangle rectangle) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return Polaroid(source, rectangle, GraphicsOptions.Default); @@ -52,10 +48,11 @@ namespace ImageSharp /// The image this method extends. /// The options effecting pixel blending. /// The . - public static Image Polaroid(this Image source, GraphicsOptions options) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel { - return Polaroid(source, source.Bounds, options); + source.ApplyProcessor(new PolaroidProcessor(options)); + return source; } /// @@ -68,7 +65,7 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Polaroid(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { source.ApplyProcessor(new PolaroidProcessor(options), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs index 88f3b1529..26ca5ec20 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,10 +21,11 @@ namespace ImageSharp /// The image this method extends. /// The new saturation of the image. Must be between -100 and 100. /// The . - public static Image Saturation(this Image source, int amount) + public static IImageProcessingContext Saturation(this IImageProcessingContext source, int amount) where TPixel : struct, IPixel { - return Saturation(source, amount, source.Bounds); + source.ApplyProcessor(new SaturationProcessor(amount)); + return source; } /// @@ -41,7 +38,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Saturation(this Image source, int amount, Rectangle rectangle) + public static IImageProcessingContext Saturation(this IImageProcessingContext source, int amount, Rectangle rectangle) where TPixel : struct, IPixel { source.ApplyProcessor(new SaturationProcessor(amount), rectangle); diff --git a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs index 000c0ffba..d1116fac8 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,11 +20,9 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Sepia(this Image source) + public static IImageProcessingContext Sepia(this IImageProcessingContext source) where TPixel : struct, IPixel - { - return Sepia(source, source.Bounds); - } + => source.ApplyProcessor(new SepiaProcessor()); /// /// Applies sepia toning to the image. @@ -39,11 +33,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Sepia(this Image source, Rectangle rectangle) + public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new SepiaProcessor(), rectangle); - return source; - } + => source.ApplyProcessor(new SepiaProcessor(), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/BoxBlur.cs b/src/ImageSharp/Processing/Convolution/BoxBlur.cs index ad5e477dc..b0c6ffc8d 100644 --- a/src/ImageSharp/Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp/Processing/Convolution/BoxBlur.cs @@ -1,22 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Applies a box blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BoxBlurProcessor(7)); + /// /// Applies a box blur to the image. /// @@ -24,11 +30,9 @@ namespace ImageSharp /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The . - public static Image BoxBlur(this Image source, int radius = 7) + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) where TPixel : struct, IPixel - { - return BoxBlur(source, radius, source.Bounds); - } + => source.ApplyProcessor(new BoxBlurProcessor(radius)); /// /// Applies a box blur to the image. @@ -40,11 +44,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); - return source; - } + => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } } diff --git a/src/ImageSharp/Processing/Convolution/DetectEdges.cs b/src/ImageSharp/Processing/Convolution/DetectEdges.cs index 1cc8b693f..79e7ac6cf 100644 --- a/src/ImageSharp/Processing/Convolution/DetectEdges.cs +++ b/src/ImageSharp/Processing/Convolution/DetectEdges.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,10 +21,10 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image DetectEdges(this Image source) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) where TPixel : struct, IPixel { - return DetectEdges(source, source.Bounds, new SobelProcessor { Grayscale = true }); + return DetectEdges(source, new SobelProcessor { Grayscale = true }); } /// @@ -41,12 +37,23 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return DetectEdges(source, rectangle, new SobelProcessor { Grayscale = true }); } + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The image this method extends. + /// The filter for detecting edges. + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetection filter) + where TPixel : struct, IPixel + => DetectEdges(source, GetProcessor(filter, true)); + /// /// Detects any edges within the image. /// @@ -55,11 +62,9 @@ namespace ImageSharp /// The filter for detecting edges. /// Whether to convert the image to Grayscale first. Defaults to true. /// The . - public static Image DetectEdges(this Image source, EdgeDetection filter, bool grayscale = true) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetection filter, bool grayscale) where TPixel : struct, IPixel - { - return DetectEdges(source, filter, source.Bounds, grayscale); - } + => DetectEdges(source, GetProcessor(filter, grayscale)); /// /// Detects any edges within the image. @@ -72,7 +77,41 @@ namespace ImageSharp /// /// Whether to convert the image to Grayscale first. Defaults to true. /// The . - public static Image DetectEdges(this Image source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) + where TPixel : struct, IPixel + => DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); + + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The image this method extends. + /// The filter for detecting edges. + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IEdgeDetectorProcessor filter) + where TPixel : struct, IPixel + { + return source.ApplyProcessor(filter); + } + + /// + /// Detects any edges within the image. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The filter for detecting edges. + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IEdgeDetectorProcessor filter) + where TPixel : struct, IPixel + { + source.ApplyProcessor(filter, rectangle); + return source; + } + + private static IEdgeDetectorProcessor GetProcessor(EdgeDetection filter, bool grayscale) where TPixel : struct, IPixel { IEdgeDetectorProcessor processor; @@ -120,37 +159,7 @@ namespace ImageSharp break; } - return DetectEdges(source, rectangle, processor); - } - - /// - /// Detects any edges within the image. - /// - /// The pixel format. - /// The image this method extends. - /// The filter for detecting edges. - /// The . - public static Image DetectEdges(this Image source, IEdgeDetectorProcessor filter) - where TPixel : struct, IPixel - { - return DetectEdges(source, source.Bounds, filter); - } - - /// - /// Detects any edges within the image. - /// - /// The pixel format. - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to alter. - /// - /// The filter for detecting edges. - /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorProcessor filter) - where TPixel : struct, IPixel - { - source.ApplyProcessor(filter, rectangle); - return source; + return processor; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs index f9658fcb5..9bca97242 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs @@ -1,23 +1,29 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Applies a Gaussian blur to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GaussianBlurProcessor(3f)); + /// /// Applies a Gaussian blur to the image. /// @@ -25,11 +31,9 @@ namespace ImageSharp /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static Image GaussianBlur(this Image source, float sigma = 3f) + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) where TPixel : struct, IPixel - { - return GaussianBlur(source, sigma, source.Bounds); - } + => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); /// /// Applies a Gaussian blur to the image. @@ -41,11 +45,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image GaussianBlur(this Image source, float sigma, Rectangle rectangle) + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); - return source; - } + => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs index bb616cc67..1cea2dae9 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs @@ -1,23 +1,29 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Applies a Gaussian sharpening filter to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GaussianSharpenProcessor(3f)); + /// /// Applies a Gaussian sharpening filter to the image. /// @@ -25,11 +31,9 @@ namespace ImageSharp /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static Image GaussianSharpen(this Image source, float sigma = 3f) + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) where TPixel : struct, IPixel - { - return GaussianSharpen(source, sigma, source.Bounds); - } + => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter to the image. @@ -41,11 +45,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image GaussianSharpen(this Image source, float sigma, Rectangle rectangle) + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); - return source; - } + => source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); } } diff --git a/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs b/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs index 809992f00..b01bb945c 100644 --- a/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs +++ b/src/ImageSharp/Processing/Convolution/Options/EdgeDetection.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the various types of defined edge detection filters. diff --git a/src/ImageSharp/Processing/Delegate.cs b/src/ImageSharp/Processing/Delegate.cs new file mode 100644 index 000000000..b390e46ae --- /dev/null +++ b/src/ImageSharp/Processing/Delegate.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Applies the given operation to the mutable image. + /// Useful when we need to extract information like Width/Height to parameterize the next operation working on the chain. + /// To achieve this the method actually implements an "inline" with as it's processing logic. + /// + /// The pixel format. + /// The image to mutate. + /// The operation to perform on the source. + /// The to allow chaining of operations. + public static IImageProcessingContext Apply(this IImageProcessingContext source, Action> operation) + where TPixel : struct, IPixel + => source.ApplyProcessor(new DelegateProcessor(operation)); + } +} diff --git a/src/ImageSharp/Processing/Effects/Alpha.cs b/src/ImageSharp/Processing/Effects/Alpha.cs index a0d218651..2fac64e1c 100644 --- a/src/ImageSharp/Processing/Effects/Alpha.cs +++ b/src/ImageSharp/Processing/Effects/Alpha.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,11 +20,9 @@ namespace ImageSharp /// The image this method extends. /// The new opacity of the image. Must be between 0 and 1. /// The . - public static Image Alpha(this Image source, float percent) + public static IImageProcessingContext Alpha(this IImageProcessingContext source, float percent) where TPixel : struct, IPixel - { - return Alpha(source, percent, source.Bounds); - } + => source.ApplyProcessor(new AlphaProcessor(percent)); /// /// Alters the alpha component of the image. @@ -40,11 +34,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Alpha(this Image source, float percent, Rectangle rectangle) + public static IImageProcessingContext Alpha(this IImageProcessingContext source, float percent, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new AlphaProcessor(percent), rectangle); - return source; - } + => source.ApplyProcessor(new AlphaProcessor(percent), rectangle); } } diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs index a1e04c8a3..da00b4ddd 100644 --- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,11 +21,9 @@ namespace ImageSharp /// The color to set as the background. /// The options effecting pixel blending. /// The . - public static Image BackgroundColor(this Image source, TPixel color, GraphicsOptions options) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - { - return BackgroundColor(source, color, source.Bounds, options); - } + => source.ApplyProcessor(new BackgroundColorProcessor(color, options)); /// /// Replaces the background color of image with the given one. @@ -42,12 +36,9 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); - return source; - } + => source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); /// /// Replaces the background color of image with the given one. @@ -56,7 +47,7 @@ namespace ImageSharp /// The image this method extends. /// The color to set as the background. /// The . - public static Image BackgroundColor(this Image source, TPixel color) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return BackgroundColor(source, color, GraphicsOptions.Default); @@ -72,7 +63,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle) + public static IImageProcessingContext BackgroundColor(this IImageProcessingContext source, TPixel color, Rectangle rectangle) where TPixel : struct, IPixel { return BackgroundColor(source, color, rectangle, GraphicsOptions.Default); diff --git a/src/ImageSharp/Processing/Effects/Brightness.cs b/src/ImageSharp/Processing/Effects/Brightness.cs index 165f897b8..5c7628785 100644 --- a/src/ImageSharp/Processing/Effects/Brightness.cs +++ b/src/ImageSharp/Processing/Effects/Brightness.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,11 +20,9 @@ namespace ImageSharp /// The image this method extends. /// The new brightness of the image. Must be between -100 and 100. /// The . - public static Image Brightness(this Image source, int amount) - where TPixel : struct, IPixel - { - return Brightness(source, amount, source.Bounds); - } + public static IImageProcessingContext Brightness(this IImageProcessingContext source, int amount) + where TPixel : struct, IPixel + => source.ApplyProcessor(new BrightnessProcessor(amount)); /// /// Alters the brightness component of the image. @@ -40,11 +34,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Brightness(this Image source, int amount, Rectangle rectangle) + public static IImageProcessingContext Brightness(this IImageProcessingContext source, int amount, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); - return source; - } + => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } } diff --git a/src/ImageSharp/Processing/Effects/Contrast.cs b/src/ImageSharp/Processing/Effects/Contrast.cs index 0a55fd067..562ab54df 100644 --- a/src/ImageSharp/Processing/Effects/Contrast.cs +++ b/src/ImageSharp/Processing/Effects/Contrast.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,11 +20,9 @@ namespace ImageSharp /// The image this method extends. /// The new contrast of the image. Must be between -100 and 100. /// The . - public static Image Contrast(this Image source, int amount) + public static IImageProcessingContext Contrast(this IImageProcessingContext source, int amount) where TPixel : struct, IPixel - { - return Contrast(source, amount, source.Bounds); - } + => source.ApplyProcessor(new ContrastProcessor(amount)); /// /// Alters the contrast component of the image. @@ -40,11 +34,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Contrast(this Image source, int amount, Rectangle rectangle) + public static IImageProcessingContext Contrast(this IImageProcessingContext source, int amount, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new ContrastProcessor(amount), rectangle); - return source; - } + => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } } diff --git a/src/ImageSharp/Processing/Effects/Invert.cs b/src/ImageSharp/Processing/Effects/Invert.cs index d9a069556..9c0a7d377 100644 --- a/src/ImageSharp/Processing/Effects/Invert.cs +++ b/src/ImageSharp/Processing/Effects/Invert.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,11 +19,9 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Invert(this Image source) + public static IImageProcessingContext Invert(this IImageProcessingContext source) where TPixel : struct, IPixel - { - return Invert(source, source.Bounds); - } + => source.ApplyProcessor(new InvertProcessor()); /// /// Inverts the colors of the image. @@ -38,11 +32,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Invert(this Image source, Rectangle rectangle) + public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - source.ApplyProcessor(new InvertProcessor(), rectangle); - return source; - } + => source.ApplyProcessor(new InvertProcessor(), rectangle); } } diff --git a/src/ImageSharp/Processing/Effects/OilPainting.cs b/src/ImageSharp/Processing/Effects/OilPainting.cs index 3b300e919..0494c9a8b 100644 --- a/src/ImageSharp/Processing/Effects/OilPainting.cs +++ b/src/ImageSharp/Processing/Effects/OilPainting.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image OilPaint(this Image source) + public static IImageProcessingContext OilPaint(this IImageProcessingContext source) where TPixel : struct, IPixel { return OilPaint(source, 10, 15); @@ -40,7 +36,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image OilPaint(this Image source, Rectangle rectangle) + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return OilPaint(source, 10, 15, rectangle); @@ -54,11 +50,9 @@ namespace ImageSharp /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. /// The . - public static Image OilPaint(this Image source, int levels, int brushSize) - where TPixel : struct, IPixel - { - return OilPaint(source, levels, brushSize, source.Bounds); - } + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) + where TPixel : struct, IPixel + => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); /// /// Alters the colors of the image recreating an oil painting effect. @@ -71,18 +65,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image OilPaint(this Image source, int levels, int brushSize, Rectangle rectangle) + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize, Rectangle rectangle) where TPixel : struct, IPixel - { - Guard.MustBeGreaterThan(levels, 0, nameof(levels)); - - if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(brushSize)); - } - - source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); - return source; - } + => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Effects/Pixelate.cs b/src/ImageSharp/Processing/Effects/Pixelate.cs index 07fdd50a3..29e348f6e 100644 --- a/src/ImageSharp/Processing/Effects/Pixelate.cs +++ b/src/ImageSharp/Processing/Effects/Pixelate.cs @@ -1,22 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// public static partial class ImageExtensions { + /// + /// Pixelates an image with the given pixel size. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static IImageProcessingContext Pixelate(this IImageProcessingContext source) + where TPixel : struct, IPixel + => source.ApplyProcessor(new PixelateProcessor(4)); + /// /// Pixelates an image with the given pixel size. /// @@ -24,11 +30,9 @@ namespace ImageSharp /// The image this method extends. /// The size of the pixels. /// The . - public static Image Pixelate(this Image source, int size = 4) + public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) where TPixel : struct, IPixel - { - return Pixelate(source, size, source.Bounds); - } + => source.ApplyProcessor(new PixelateProcessor(size)); /// /// Pixelates an image with the given pixel size. @@ -40,16 +44,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Pixelate(this Image source, int size, Rectangle rectangle) + public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size, Rectangle rectangle) where TPixel : struct, IPixel - { - if (size <= 0 || size > source.Height || size > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(size)); - } - - source.ApplyProcessor(new PixelateProcessor(size), rectangle); - return source; - } + => source.ApplyProcessor(new PixelateProcessor(size), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/ImageProcessor.cs b/src/ImageSharp/Processing/ImageProcessor.cs deleted file mode 100644 index dd2a93bc5..000000000 --- a/src/ImageSharp/Processing/ImageProcessor.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// Allows the application of processors to images. - /// - /// The pixel format. - internal abstract class ImageProcessor : IImageProcessor - where TPixel : struct, IPixel - { - /// - public virtual ParallelOptions ParallelOptions { get; set; } - - /// - public virtual bool Compand { get; set; } = false; - - /// - public void Apply(ImageBase source, Rectangle sourceRectangle) - { - if (this.ParallelOptions == null) - { - this.ParallelOptions = source.Configuration.ParallelOptions; - } - - try - { - this.BeforeApply(source, sourceRectangle); - - this.OnApply(source, sourceRectangle); - - this.AfterApply(source, sourceRectangle); - } -#if DEBUG - catch (Exception) - { - throw; -#else - catch (Exception ex) - { - throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); -#endif - } - } - - /// - /// This method is called before the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void BeforeApply(ImageBase source, Rectangle sourceRectangle) - { - } - - /// - /// Applies the process to the specified portion of the specified at the specified location - /// and with the specified size. - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected abstract void OnApply(ImageBase source, Rectangle sourceRectangle); - - /// - /// This method is called after the process is applied to prepare the processor. - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - protected virtual void AfterApply(ImageBase source, Rectangle sourceRectangle) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index 84f6bf10a..ee3596348 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -21,7 +18,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Glow(this Image source) + public static IImageProcessingContext Glow(this IImageProcessingContext source) where TPixel : struct, IPixel { return Glow(source, GraphicsOptions.Default); @@ -34,7 +31,7 @@ namespace ImageSharp /// The image this method extends. /// The color to set as the glow. /// The . - public static Image Glow(this Image source, TPixel color) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return Glow(source, color, GraphicsOptions.Default); @@ -47,7 +44,7 @@ namespace ImageSharp /// The image this method extends. /// The the radius. /// The . - public static Image Glow(this Image source, float radius) + public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) where TPixel : struct, IPixel { return Glow(source, radius, GraphicsOptions.Default); @@ -62,11 +59,9 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Glow(this Image source, Rectangle rectangle) + public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel - { - return Glow(source, rectangle, GraphicsOptions.Default); - } + => source.Glow(rectangle, GraphicsOptions.Default); /// /// Applies a radial glow effect to an image. @@ -79,11 +74,9 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle) where TPixel : struct, IPixel - { - return Glow(source, color, radius, rectangle, GraphicsOptions.Default); - } + => source.Glow(color, ValueSize.Absolute(radius), rectangle, GraphicsOptions.Default); /// /// Applies a radial glow effect to an image. @@ -92,11 +85,9 @@ namespace ImageSharp /// The image this method extends. /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds, options); - } + => source.Glow(NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), options); /// /// Applies a radial glow effect to an image. @@ -106,11 +97,9 @@ namespace ImageSharp /// The color to set as the glow. /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, TPixel color, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, color, source.Bounds.Width * .5F, source.Bounds, options); - } + => source.Glow(color, ValueSize.PercentageOfWidth(0.5f), options); /// /// Applies a radial glow effect to an image. @@ -120,11 +109,9 @@ namespace ImageSharp /// The the radius. /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, float radius, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, NamedColors.Black, radius, source.Bounds, options); - } + => source.Glow(NamedColors.Black, ValueSize.Absolute(radius), options); /// /// Applies a radial glow effect to an image. @@ -136,11 +123,9 @@ namespace ImageSharp /// /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - return Glow(source, NamedColors.Black, 0, rectangle, options); - } + => source.Glow(NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), rectangle, options); /// /// Applies a radial glow effect to an image. @@ -154,12 +139,37 @@ namespace ImageSharp /// /// The options effecting things like blending. /// The . - public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - var processor = new GlowProcessor(color, options) { Radius = radius, }; - source.ApplyProcessor(processor, rectangle); - return source; - } + => source.Glow(color, ValueSize.Absolute(radius), rectangle, options); + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the glow. + /// The the radius. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The options effecting things like blending. + /// The . + private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the glow. + /// The the radius. + /// The options effecting things like blending. + /// The . + private static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, ValueSize radius, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new GlowProcessor(color, radius, options)); } } diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs index 75c4611a1..cc93ccedc 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -21,7 +18,7 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The . - public static Image Vignette(this Image source) + public static IImageProcessingContext Vignette(this IImageProcessingContext source) where TPixel : struct, IPixel { return Vignette(source, GraphicsOptions.Default); @@ -34,7 +31,7 @@ namespace ImageSharp /// The image this method extends. /// The color to set as the vignette. /// The . - public static Image Vignette(this Image source, TPixel color) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { return Vignette(source, color, GraphicsOptions.Default); @@ -48,7 +45,7 @@ namespace ImageSharp /// The the x-radius. /// The the y-radius. /// The . - public static Image Vignette(this Image source, float radiusX, float radiusY) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY) where TPixel : struct, IPixel { return Vignette(source, radiusX, radiusY, GraphicsOptions.Default); @@ -63,7 +60,7 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Vignette(this Image source, Rectangle rectangle) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel { return Vignette(source, rectangle, GraphicsOptions.Default); @@ -81,11 +78,9 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to alter. /// /// The . - public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) where TPixel : struct, IPixel - { - return Vignette(source, color, radiusX, radiusY, rectangle, GraphicsOptions.Default); - } + => source.Vignette(color, radiusX, radiusY, rectangle, GraphicsOptions.Default); /// /// Applies a radial vignette effect to an image. @@ -94,11 +89,9 @@ namespace ImageSharp /// The image this method extends. /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options); - } + => source.VignetteInternal(NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), options); /// /// Applies a radial vignette effect to an image. @@ -108,11 +101,9 @@ namespace ImageSharp /// The color to set as the vignette. /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, TPixel color, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options); - } + => source.VignetteInternal(color, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), options); /// /// Applies a radial vignette effect to an image. @@ -123,11 +114,9 @@ namespace ImageSharp /// The the y-radius. /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, float radiusX, float radiusY, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds, options); - } + => source.VignetteInternal(NamedColors.Black, radiusX, radiusY, options); /// /// Applies a radial vignette effect to an image. @@ -139,11 +128,9 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - return Vignette(source, NamedColors.Black, 0, 0, rectangle, options); - } + => source.VignetteInternal(NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), rectangle, options); /// /// Applies a radial vignette effect to an image. @@ -158,12 +145,16 @@ namespace ImageSharp /// /// The options effecting pixel blending. /// The . - public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options) + public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel - { - var processor = new VignetteProcessor(color, options) { RadiusX = radiusX, RadiusY = radiusY }; - source.ApplyProcessor(processor, rectangle); - return source; - } + => source.VignetteInternal(color, radiusX, radiusY, rectangle, options); + + private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options), rectangle); + + private static IImageProcessingContext VignetteInternal(this IImageProcessingContext source, TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) + where TPixel : struct, IPixel + => source.ApplyProcessor(new VignetteProcessor(color, radiusX, radiusY, options)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index ea1b759ab..d736b91ee 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to perform binary threshold filtering against an /// . The image will be converted to grayscale before thresholding occurs. @@ -50,13 +48,13 @@ namespace ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float threshold = this.Threshold; TPixel upper = this.UpperColor; @@ -87,10 +85,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span row = source.GetRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { diff --git a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index 85522e288..8907770e1 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that dithers an image using error diffusion. /// @@ -56,47 +55,29 @@ namespace ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; - for (int y = minY; y < maxY; y++) + for (int y = startY; y < endY; y++) { - int offsetY = y - startY; - Span row = source.GetRowSpan(offsetY); + Span row = source.GetPixelRowSpan(y); - for (int x = minX; x < maxX; x++) + for (int x = startX; x < endX; x++) { - int offsetX = x - startX; - TPixel sourceColor = row[offsetX]; + TPixel sourceColor = row[x]; TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor; - this.Diffuser.Dither(source, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY); + this.Diffuser.Dither(source, sourceColor, transformedColor, x, y, startX, startY, endX, endY); } } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs index a190bcd61..203a64cf1 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Buffers; - - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Buffers; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that dithers an image using error diffusion. /// @@ -64,47 +63,29 @@ namespace ImageSharp.Processing.Processors public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; byte[] bytes = new byte[4]; - for (int y = minY; y < maxY; y++) + for (int y = startY; y < endY; y++) { - int offsetY = y - startY; - Span row = source.GetRowSpan(offsetY); + Span row = source.GetPixelRowSpan(y); - for (int x = minX; x < maxX; x++) + for (int x = startX; x < endX; x++) { - int offsetX = x - startX; - TPixel sourceColor = row[offsetX]; - this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY); + TPixel sourceColor = row[x]; + this.Dither.Dither(source, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, x, y); } } } diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs new file mode 100644 index 000000000..fdee21ed6 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -0,0 +1,141 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Allows the application of processors to images. + /// + /// The pixel format. + internal abstract class CloningImageProcessor : ICloningImageProcessor + where TPixel : struct, IPixel + { + /// + public Image CloneAndApply(Image source, Rectangle sourceRectangle) + { + try + { + Image clone = this.CreateDestination(source, sourceRectangle); + + if (clone.Frames.Count != source.Frames.Count) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); + } + + Configuration configuration = source.GetConfiguration(); + this.BeforeImageApply(source, clone, sourceRectangle); + + for (int i = 0; i < source.Frames.Count; i++) + { + ImageFrame sourceFrame = source.Frames[i]; + ImageFrame clonedFrame = clone.Frames[i]; + + this.BeforeApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + this.OnApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + this.AfterApply(sourceFrame, clonedFrame, sourceRectangle, configuration); + } + + this.AfterImageApply(source, clone, sourceRectangle); + + return clone; + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + public void Apply(Image source, Rectangle sourceRectangle) + { + using (Image cloned = this.CloneAndApply(source, sourceRectangle)) + { + // we now need to move the pixel data/size data from one image base to another + if (cloned.Frames.Count != source.Frames.Count) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. The processor changed the number of frames."); + } + + source.SwapPixelsBuffers(cloned); + } + } + + /// + /// Generates a deep clone of the source image that operatinos should be applied to. + /// + /// The source image. Cannot be null. + /// The source rectangle. + /// The cloned image. + protected virtual Image CreateDestination(Image source, Rectangle sourceRectangle) + { + return source.Clone(); + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + protected virtual void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected abstract void OnApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration); + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The cloned/destination image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + protected virtual void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs index d37d119a4..9f8127343 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image to their black and white equivalent. /// diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs index a91bd1946..91e5c7f68 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = .618F, M12 = .163F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs index d543c4edc..0d6578852 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = .299F, M12 = .299F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs index ea73d0c66..c4bb41ceb 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.8F, M12 = 0.258F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs index 4b5129a8b..598af12ff 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.625F, M12 = 0.7F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs index 14eea0812..4e32cb529 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.817F, M12 = 0.333F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs index 39cb715bd..d49b4a2cc 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.567F, M12 = 0.558F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs index 2b402197b..d34f22343 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.967F, M21 = 0.33F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs index 5d228afa7..453ac99a7 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.95F, M21 = 0.05F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs index 1bac145bc..4a64bfaa0 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs @@ -1,32 +1,30 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The color matrix filter. Inherit from this class to perform operation involving color matrices. /// /// The pixel format. - internal abstract class ColorMatrixProcessor : ImageProcessor, IColorMatrixFilter + internal abstract class ColorMatrixProcessor : ImageProcessor, IColorMatrixProcessor where TPixel : struct, IPixel { /// public abstract Matrix4x4 Matrix { get; } /// - public override bool Compand { get; set; } = true; + public virtual bool Compand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -53,31 +51,28 @@ namespace ImageSharp.Processing.Processors Matrix4x4 matrix = this.Matrix; bool compand = this.Compand; - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - Span row = source.GetRowSpan(y - startY); + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + Span row = source.GetPixelRowSpan(y - startY); - for (int x = minX; x < maxX; x++) - { - ref TPixel pixel = ref row[x - startX]; - var vector = pixel.ToVector4(); + for (int x = minX; x < maxX; x++) + { + ref TPixel pixel = ref row[x - startX]; + var vector = pixel.ToVector4(); - if (compand) - { - vector = vector.Expand(); - } + if (compand) + { + vector = vector.Expand(); + } - vector = Vector4.Transform(vector, matrix); - pixel.PackFromVector4(compand ? vector.Compress() : vector); - } - }); - } + vector = Vector4.Transform(vector, matrix); + pixel.PackFromVector4(compand ? vector.Compress() : vector); + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs index 65de8a66d..35dfe41a8 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.601 /// . @@ -19,7 +15,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = .299F, M12 = .299F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs index 5f71e357b..6bb460ee6 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709 /// . @@ -19,7 +15,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = .2126F, M12 = .2126F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs index f28683977..9a40bfca8 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to change the hue of an . /// @@ -49,7 +45,7 @@ namespace ImageSharp.Processing.Processors // The matrix is set up to preserve the luminance of the image. // See http://graficaobscura.com/matrix/index.html // Number are taken from https://msdn.microsoft.com/en-us/library/jj192162(v=vs.85).aspx - Matrix4x4 matrix4X4 = new Matrix4x4() + var matrix4X4 = new Matrix4x4 { M11 = lumR + (cosradians * oneMinusLumR) - (sinradians * lumR), M12 = lumR - (cosradians * lumR) - (sinradians * 0.143F), diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs similarity index 52% rename from src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs rename to src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs index 0c29c65a1..84e7461b5 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixProcessor.cs @@ -1,26 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Encapsulates properties and methods for creating processors that utilize a matrix to /// alter the image pixels. /// /// The pixel format. - internal interface IColorMatrixFilter : IImageProcessor + internal interface IColorMatrixProcessor : IImageProcessor where TPixel : struct, IPixel { /// /// Gets the used to alter the image. /// Matrix4x4 Matrix { get; } + + /// + /// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing. + /// + bool Compand { get; set; } } } diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs index 18514236f..4277e1fc2 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating an old Kodachrome camera effect. /// @@ -18,7 +14,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 0.6997023F, M22 = 0.4609577F, diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs index bb33e5151..1ec76bf55 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating an old Lomograph effect. /// @@ -19,7 +15,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - private GraphicsOptions options; + private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. @@ -31,7 +27,7 @@ namespace ImageSharp.Processing.Processors } /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 1.5F, M22 = 1.45F, @@ -43,9 +39,9 @@ namespace ImageSharp.Processing.Processors }; /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs index 9e54574df..f910562e6 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image recreating an old Polaroid effect. /// @@ -18,9 +14,9 @@ namespace ImageSharp.Processing.Processors internal class PolaroidProcessor : ColorMatrixProcessor where TPixel : struct, IPixel { - private static TPixel veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static TPixel lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); - private GraphicsOptions options; + private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); + private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. @@ -32,7 +28,7 @@ namespace ImageSharp.Processing.Processors } /// - public override Matrix4x4 Matrix => new Matrix4x4() + public override Matrix4x4 Matrix => new Matrix4x4 { M11 = 1.538F, M12 = -0.062F, @@ -50,10 +46,10 @@ namespace ImageSharp.Processing.Processors }; /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new VignetteProcessor(veryDarkOrange, this.options).Apply(source, sourceRectangle); - new GlowProcessor(lightOrange, this.options) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkOrange, this.options).Apply(source, sourceRectangle, configuration); + new GlowProcessor(LightOrange, source.Width / 4F, this.options).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs index 3adfb8311..1f01bc85d 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to change the saturation of an . /// @@ -26,6 +22,7 @@ namespace ImageSharp.Processing.Processors /// public SaturationProcessor(int saturation) { + this.Amount = saturation; Guard.MustBeBetweenOrEqualTo(saturation, -100, 100, nameof(saturation)); float saturationFactor = saturation / 100F; @@ -41,7 +38,7 @@ namespace ImageSharp.Processing.Processors float saturationComplementG = 0.6094f * saturationComplement; float saturationComplementB = 0.0820f * saturationComplement; - Matrix4x4 matrix4X4 = new Matrix4x4 + var matrix4X4 = new Matrix4x4 { M11 = saturationComplementR + saturationFactor, M12 = saturationComplementR, @@ -58,6 +55,11 @@ namespace ImageSharp.Processing.Processors this.Matrix = matrix4X4; } + /// + /// Gets the amount to apply. + /// + public int Amount { get; } + /// public override Matrix4x4 Matrix { get; } } diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs index 89be3acad..d959ebf52 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Converts the colors of the image to their sepia equivalent. /// The formula used matches the svg specification. diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 0a2162fb0..8056141a0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Applies a Box blur sampler to the image. /// @@ -31,11 +28,17 @@ namespace ImageSharp.Processing.Processors /// public BoxBlurProcessor(int radius = 7) { + this.Radius = radius; this.kernelSize = (radius * 2) + 1; this.KernelX = this.CreateBoxKernel(true); this.KernelY = this.CreateBoxKernel(false); } + /// + /// Gets the Radius + /// + public int Radius { get; } + /// /// Gets the horizontal gradient operator. /// @@ -47,9 +50,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index 708b6c6fd..b85432ac5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that uses two one-dimensional matrices to perform convolution against an image. /// @@ -42,7 +40,7 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; @@ -65,10 +63,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( startY, endY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -87,7 +85,7 @@ namespace ImageSharp.Processing.Processors int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelXWidth; fx++) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index ceb985b0b..362fa5c50 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that uses two one-dimensional matrices to perform two-pass convolution against an image. /// @@ -42,21 +41,22 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int width = source.Width; int height = source.Height; + ParallelOptions parallelOptions = configuration.ParallelOptions; using (var firstPassPixels = new PixelAccessor(width, height)) using (PixelAccessor sourcePixels = source.Lock()) { - this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds, this.KernelX); - this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY); + this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds(), this.KernelX, parallelOptions); + this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY, parallelOptions); } } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The target pixels to apply the process to. @@ -65,7 +65,13 @@ namespace ImageSharp.Processing.Processors /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel) + /// The parellel options + private void ApplyConvolution( + PixelAccessor targetPixels, + PixelAccessor sourcePixels, + Rectangle sourceRectangle, + Fast2DArray kernel, + ParallelOptions parallelOptions) { int kernelHeight = kernel.Height; int kernelWidth = kernel.Width; @@ -82,7 +88,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( startY, endY, - this.ParallelOptions, + parallelOptions, y => { Span targetRow = targetPixels.GetRowSpan(y); diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index cd2eb2700..c0d3fdcfe 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that uses a 2 dimensional matrix to perform convolution against an image. /// @@ -35,7 +33,7 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int kernelLength = this.KernelXY.Height; int radius = kernelLength >> 1; @@ -54,10 +52,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( startY, endY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -73,7 +71,7 @@ namespace ImageSharp.Processing.Processors int offsetY = y + fyr; offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx < kernelLength; fx++) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index ab0b45907..f93787d12 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that detects edges within an image using two one-dimensional matrices. /// @@ -43,17 +40,17 @@ namespace ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 367c288fc..32c22a8ce 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that detects edges within an image using a eight two dimensional matrices. /// @@ -64,16 +62,16 @@ namespace ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; @@ -89,9 +87,9 @@ namespace ImageSharp.Processing.Processors int maxY = Math.Min(source.Height, endY); // we need a clean copy for each pass to start from - using (ImageBase cleanCopy = new Image(source)) + using (ImageFrame cleanCopy = source.Clone()) { - new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle); + new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle, configuration); if (kernels.Length == 1) { @@ -116,9 +114,9 @@ namespace ImageSharp.Processing.Processors // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) { - using (ImageBase pass = new Image(cleanCopy)) + using (ImageFrame pass = cleanCopy.Clone()) { - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle, configuration); using (PixelAccessor passPixels = pass.Lock()) using (PixelAccessor targetPixels = source.Lock()) @@ -126,7 +124,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - shiftY; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index 1400b6317..3b98b77fc 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Defines a sampler that detects edges within an image using a single two dimensional matrix. /// @@ -36,18 +33,18 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle, configuration); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); + new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs index c7c126794..6208a24a4 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing +{ /// /// Provides properties and methods allowing the detection of edges within an image. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index b1361b514..8e4cdd6b5 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Kayyali operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index af4700bb9..c0a3b3595 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Kirsch operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index 5f58d56f8..1b5c56318 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Laplacian 3 x 3 operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index 2e365374c..11438fe8d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Laplacian 5 x 5 operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index de2594653..424c01137 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Laplacian of Gaussian operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 1b2d5f50e..0d2e9b206 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Prewitt operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index d1b9614b6..09aa8ecb6 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Roberts Cross operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index bc687f674..18f8cbf18 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Kirsch operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index 339b9741f..e299d3f80 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Scharr operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index b9060ecbc..e975e4ff6 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +using System; +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// The Sobel operator filter. /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index ef6ddaa6a..c897efeed 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Applies a Gaussian blur sampler to the image. /// @@ -72,6 +69,11 @@ namespace ImageSharp.Processing.Processors this.KernelY = this.CreateGaussianKernel(false); } + /// + /// Gets the sigma + /// + public float Sigma => this.sigma; + /// /// Gets the horizontal gradient operator. /// @@ -83,9 +85,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 594dda8ca..b960e9075 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Applies a Gaussian sharpening sampler to the image. /// @@ -74,6 +71,11 @@ namespace ImageSharp.Processing.Processors this.KernelY = this.CreateGaussianKernel(false); } + /// + /// Gets the sigma + /// + public float Sigma => this.sigma; + /// /// Gets the horizontal gradient operator. /// @@ -85,9 +87,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); } /// diff --git a/src/ImageSharp/Processing/Processors/DelegateProcessor.cs b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs new file mode 100644 index 000000000..f17c39681 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/DelegateProcessor.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Allows the application of processors to images. + /// + /// The pixel format. + internal class DelegateProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly Action> action; + + /// + /// Initializes a new instance of the class. + /// + /// The action. + public DelegateProcessor(Action> action) + { + this.action = action; + } + + /// + /// Gets the action that will be applied to the image. + /// + internal Action> Action => this.action; + + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + this.action?.Invoke(source); + } + + /// + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // NOP, we did all we wanted to do inside BeforeImageApply + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs index 0efca43b2..7e5bd02ab 100644 --- a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to change the alpha component of an . /// @@ -38,7 +36,7 @@ namespace ImageSharp.Processing.Processors public float Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -67,10 +65,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span row = source.GetRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index 96fcf4d63..72e9b8f55 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Sets the background color of the image. /// @@ -32,13 +30,18 @@ namespace ImageSharp.Processing.Processors this.options = options; } + /// + /// Gets the Graphics options to alter how processor is applied. + /// + public GraphicsOptions GraphicsOptions => this.options; + /// /// Gets the background color value. /// public TPixel Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -77,10 +80,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span destination = source.GetRowSpan(y - startY).Slice(minX - startX, width); + Span destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one blender.Blend(destination, colors, destination, amount); diff --git a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs index 3fbe1742e..c864330c9 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to change the brightness of an . /// @@ -38,7 +36,7 @@ namespace ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float brightness = this.Value / 100F; @@ -67,10 +65,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span row = source.GetRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { diff --git a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs index e40f8d5de..5ab266211 100644 --- a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to change the contrast of an . /// @@ -38,7 +36,7 @@ namespace ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { float contrast = (100F + this.Value) / 100F; @@ -69,10 +67,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span row = source.GetRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { diff --git a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs index 07a57db54..448025f70 100644 --- a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to invert the colors of an . /// @@ -20,7 +18,7 @@ namespace ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -48,10 +46,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span row = source.GetRowSpan(y - startY); + Span row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index f484c8eec..b22a49798 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to apply an oil painting effect to an . /// @@ -49,38 +48,34 @@ namespace ImageSharp.Processing.Processors public int BrushSize { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(this.BrushSize)); + } + int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + int radius = this.BrushSize >> 1; int levels = this.Levels; - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - using (var targetPixels = new PixelAccessor(source.Width, source.Height)) { source.CopyTo(targetPixels); Parallel.For( - minY, + startY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -100,7 +95,7 @@ namespace ImageSharp.Processing.Processors offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); for (int fx = 0; fx <= radius; fx++) { diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index ff83117c5..0ab21f65a 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Common; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An to pixelate the colors of an . /// @@ -29,23 +28,28 @@ namespace ImageSharp.Processing.Processors public PixelateProcessor(int size) { Guard.MustBeGreaterThan(size, 0, nameof(size)); - this.Value = size; + this.Size = size; } /// /// Gets or the pixel size. /// - public int Value { get; } + public int Size { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(this.Size)); + } + int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - int size = this.Value; - int offset = this.Value / 2; + int size = this.Size; + int offset = this.Size / 2; // Align start/end positions. int minX = Math.Max(0, startX); @@ -69,7 +73,7 @@ namespace ImageSharp.Processing.Processors Parallel.ForEach( range, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; @@ -81,7 +85,7 @@ namespace ImageSharp.Processing.Processors offsetPy--; } - Span row = source.GetRowSpan(offsetY + offsetPy); + Span row = source.GetPixelRowSpan(offsetY + offsetPy); for (int x = minX; x < maxX; x += size) { diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor.cs b/src/ImageSharp/Processing/Processors/ImageProcessor.cs new file mode 100644 index 000000000..cab99112c --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ImageProcessor.cs @@ -0,0 +1,118 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Allows the application of processors to images. + /// + /// The pixel format. + internal abstract class ImageProcessor : IImageProcessor + where TPixel : struct, IPixel + { + /// + public void Apply(Image source, Rectangle sourceRectangle) + { + try + { + Configuration config = source.GetConfiguration(); + this.BeforeImageApply(source, sourceRectangle); + + foreach (ImageFrame sourceFrame in source.Frames) + { + this.Apply(sourceFrame, sourceRectangle, config); + } + + this.AfterImageApply(source, sourceRectangle); + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + /// Applies the processor to just a single ImageBase + /// + /// the source image + /// the target + /// The configuration. + public void Apply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + try + { + this.BeforeApply(source, sourceRectangle, configuration); + this.OnApply(source, sourceRectangle, configuration); + this.AfterApply(source, sourceRectangle, configuration); + } +#if DEBUG + catch (Exception) + { + throw; +#else + catch (Exception ex) + { + throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif + } + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + protected virtual void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + } + + /// + /// This method is called before the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// Applies the process to the specified portion of the specified at the specified location + /// and with the specified size. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected abstract void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration); + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + /// The configuration. + protected virtual void AfterApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + } + + /// + /// This method is called after the process is applied to prepare the processor. + /// + /// The source image. Cannot be null. + /// The structure that specifies the portion of the image object to draw. + protected virtual void AfterImageApply(Image source, Rectangle sourceRectangle) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 8a13eabca..b02585d8f 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that applies a radial glow effect an . /// @@ -27,14 +26,21 @@ namespace ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The color or the glow. + /// The radius of the glow. /// The options effecting blending and composition. - public GlowProcessor(TPixel color, GraphicsOptions options) + public GlowProcessor(TPixel color, ValueSize radius, GraphicsOptions options) { this.options = options; this.GlowColor = color; + this.Radius = radius; this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } + /// + /// Gets the Graphics options to alter how processor is applied. + /// + public GraphicsOptions GraphicsOptions => this.options; + /// /// Gets or sets the glow color to apply. /// @@ -43,10 +49,10 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the the radius. /// - public float Radius { get; set; } + public ValueSize Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -54,7 +60,10 @@ namespace ImageSharp.Processing.Processors int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle); - float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; + + var finalRadius = this.Radius.Calculate(source.Size()); + + float maxDistance = finalRadius > 0 ? MathF.Min(finalRadius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. int minX = Math.Max(0, startX); @@ -84,7 +93,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { using (var amounts = new Buffer(width)) @@ -97,7 +106,7 @@ namespace ImageSharp.Processing.Processors amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); } - Span destination = source.GetRowSpan(offsetY).Slice(offsetX, width); + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); this.blender.Blend(destination, destination, rowColors, amounts); } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 8cb58739b..7b592a6a4 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// An that applies a radial vignette effect to an . /// @@ -27,15 +26,35 @@ namespace ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The color of the vignette. + /// The x-radius. + /// The y-radius. /// The options effecting blending and composition. - public VignetteProcessor(TPixel color, GraphicsOptions options) + public VignetteProcessor(TPixel color, ValueSize radiusX, ValueSize radiusY, GraphicsOptions options) { this.VignetteColor = color; + this.RadiusX = radiusX; + this.RadiusY = radiusY; + this.options = options; + this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); + } + /// + /// Initializes a new instance of the class. + /// + /// The color of the vignette. + /// The options effecting blending and composition. + public VignetteProcessor(TPixel color, GraphicsOptions options) + { + this.VignetteColor = color; this.options = options; this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } + /// + /// Gets the Graphics options to alter how processor is applied. + /// + public GraphicsOptions GraphicsOptions => this.options; + /// /// Gets or sets the vignette color to apply. /// @@ -44,15 +63,15 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the the x-radius. /// - public float RadiusX { get; set; } + public ValueSize RadiusX { get; set; } /// /// Gets or sets the the y-radius. /// - public float RadiusY { get; set; } + public ValueSize RadiusY { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -60,8 +79,11 @@ namespace ImageSharp.Processing.Processors int endX = sourceRectangle.Right; TPixel vignetteColor = this.VignetteColor; Vector2 centre = Rectangle.Center(sourceRectangle); - float rX = this.RadiusX > 0 ? MathF.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; - float rY = this.RadiusY > 0 ? MathF.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; + + var finalradiusX = this.RadiusX.Calculate(source.Size()); + var finalradiusY = this.RadiusY.Calculate(source.Size()); + float rX = finalradiusX > 0 ? MathF.Min(finalradiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; + float rY = finalradiusY > 0 ? MathF.Min(finalradiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); // Align start/end positions. @@ -92,7 +114,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { using (var amounts = new Buffer(width)) @@ -105,7 +127,7 @@ namespace ImageSharp.Processing.Processors amounts[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1); } - Span destination = source.GetRowSpan(offsetY).Slice(offsetX, width); + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); this.blender.Blend(destination, destination, rowColors, amounts); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs new file mode 100644 index 000000000..ab93e0e38 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. + /// + /// The pixel format. + internal class AutoOrientProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + public AutoOrientProcessor() + { + } + + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + Orientation orientation = GetExifOrientation(source); + + switch (orientation) + { + case Orientation.TopRight: + new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); + break; + + case Orientation.BottomRight: + new RotateProcessor() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.BottomLeft: + new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); + break; + + case Orientation.LeftTop: + new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); + new FlipProcessor(FlipType.Horizontal).Apply(source, sourceRectangle); + break; + + case Orientation.RightTop: + new RotateProcessor() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.RightBottom: + new FlipProcessor(FlipType.Vertical).Apply(source, sourceRectangle); + new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.LeftBottom: + new RotateProcessor() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle); + break; + + case Orientation.Unknown: + case Orientation.TopLeft: + default: + break; + } + } + + /// + protected override void OnApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + { + // all processing happens at the image level within BeforeImageApply(); + } + + /// + /// Returns the current EXIF orientation + /// + /// The image to auto rotate. + /// The + private static Orientation GetExifOrientation(Image source) + { + if (source.MetaData.ExifProfile == null) + { + return Orientation.Unknown; + } + + ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); + if (value == null) + { + return Orientation.Unknown; + } + + Orientation orientation; + if (value.DataType == ExifDataType.Short) + { + orientation = (Orientation)value.Value; + } + else + { + orientation = (Orientation)Convert.ToUInt16(value.Value); + source.MetaData.ExifProfile.RemoveValue(ExifTag.Orientation); + } + + source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); + + return orientation; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 37f886775..2657daaa8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods to allow the cropping of an image. /// @@ -33,7 +32,7 @@ namespace ImageSharp.Processing.Processors public Rectangle CropRectangle { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (this.CropRectangle == sourceRectangle) { @@ -50,10 +49,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(minX, y); + Span sourceRow = source.GetPixelRowSpan(y).Slice(minX); Span targetRow = targetPixels.GetRowSpan(y - minY); SpanHelper.Copy(sourceRow, targetRow, maxX - minX); }); diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 16f74f218..d2a08daba 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods to allow the cropping of an image to preserve areas of highest /// entropy. @@ -28,24 +25,24 @@ namespace ImageSharp.Processing.Processors public EntropyCropProcessor(float threshold) { Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); - this.Value = threshold; + this.Threshold = threshold; } /// /// Gets the threshold value. /// - public float Value { get; } + public float Threshold { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - using (ImageBase temp = new Image(source)) + using (ImageFrame temp = source.Clone()) { // Detect the edges. - new SobelProcessor().Apply(temp, sourceRectangle); + new SobelProcessor().Apply(temp, sourceRectangle, configuration); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); + new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); @@ -55,7 +52,7 @@ namespace ImageSharp.Processing.Processors return; } - new CropProcessor(rectangle).Apply(source, sourceRectangle); + new CropProcessor(rectangle).Apply(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index cba60f928..de60177f2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the flipping of an image around its center point. /// @@ -34,16 +32,16 @@ namespace ImageSharp.Processing.Processors public FlipType FlipType { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { switch (this.FlipType) { // No default needed as we have already set the pixels. case FlipType.Vertical: - this.FlipX(source); + this.FlipX(source, configuration); break; case FlipType.Horizontal: - this.FlipY(source); + this.FlipY(source, configuration); break; } } @@ -53,7 +51,8 @@ namespace ImageSharp.Processing.Processors /// at half the height of the image. /// /// The source image to apply the process to. - private void FlipX(ImageBase source) + /// The configuration. + private void FlipX(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -64,12 +63,12 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, halfHeight, - this.ParallelOptions, + configuration.ParallelOptions, y => { int newY = height - y - 1; - Span sourceRow = source.GetRowSpan(y); - Span altSourceRow = source.GetRowSpan(newY); + Span sourceRow = source.GetPixelRowSpan(y); + Span altSourceRow = source.GetPixelRowSpan(newY); Span targetRow = targetPixels.GetRowSpan(y); Span altTargetRow = targetPixels.GetRowSpan(newY); @@ -86,7 +85,8 @@ namespace ImageSharp.Processing.Processors /// at half of the width of the image. /// /// The source image to apply the process to. - private void FlipY(ImageBase source) + /// The configuration. + private void FlipY(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -97,10 +97,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(y); for (int x = 0; x < halfWidth; x++) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs index d1a35659b..4a15254ab 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods to transform an image using a . /// @@ -43,7 +40,7 @@ namespace ImageSharp.Processing.Processors /// /// The . /// - protected Matrix3x2 GetCenteredMatrix(ImageBase source, Matrix3x2 matrix) + protected Matrix3x2 GetCenteredMatrix(ImageFrame source, Matrix3x2 matrix) { var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F); var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 8aef87ec8..1169d2ead 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.Memory; +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Conains the definition of and . /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 0186a8fa8..781f3a01c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -1,22 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the resizing of images using various algorithms. /// Adapted from /// /// The pixel format. - internal abstract partial class ResamplingWeightedProcessor : ImageProcessor + internal abstract partial class ResamplingWeightedProcessor : CloningImageProcessor where TPixel : struct, IPixel { /// @@ -46,19 +43,19 @@ namespace ImageSharp.Processing.Processors public IResampler Sampler { get; } /// - /// Gets the width. + /// Gets or sets the width. /// - public int Width { get; } + public int Width { get; protected set; } /// - /// Gets the height. + /// Gets or sets the height. /// - public int Height { get; } + public int Height { get; protected set; } /// - /// Gets the resize rectangle. + /// Gets or sets the resize rectangle. /// - public Rectangle ResizeRectangle { get; } + public Rectangle ResizeRectangle { get; protected set; } /// /// Gets or sets the horizontal weights. @@ -140,7 +137,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { if (!(this.Sampler is NearestNeighborResampler)) { @@ -155,9 +152,9 @@ namespace ImageSharp.Processing.Processors } /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - base.AfterApply(source, sourceRectangle); + base.AfterApply(source, destination, sourceRectangle, configuration); this.HorizontalWeights?.Dispose(); this.HorizontalWeights = null; this.VerticalWeights?.Dispose(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index be1680cf1..a4fdb1a1b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -1,18 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors { - using System; - using System.Numerics; - using System.Threading.Tasks; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - /// /// Provides methods that allow the resizing of images using various algorithms. /// @@ -45,12 +45,34 @@ namespace ImageSharp.Processing.Processors { } + /// + /// Gets or sets a value indicating whether to compress or expand individual pixel color values on processing. + /// + public bool Compand { get; set; } + /// - protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + Configuration config = source.GetConfiguration(); + + // We will always be creating the clone even for mutate because thats the way this base processor works + // ------------ + // For resize we know we are going to populate every pixel with fresh data and we want a different target size so + // let's manually clone an empty set of images at the correct target and then have the base class processs them in turn. + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders + var image = new Image(config, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added + + return image; + } + + /// + protected override unsafe void OnApply(ImageFrame source, ImageFrame cloned, Rectangle sourceRectangle, Configuration configuration) { // Jump out, we'll deal with that later. - if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) + if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle) { + // the cloned will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(cloned.GetPixelSpan()); return; } @@ -74,113 +96,102 @@ namespace ImageSharp.Processing.Processors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; - using (var targetPixels = new PixelAccessor(width, height)) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + // Y coordinates of source points + Span sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span targetRow = cloned.GetPixelRowSpan(y); + + for (int x = minX; x < maxX; x++) { - // Y coordinates of source points - Span sourceRow = source.GetRowSpan((int)(((y - startY) * heightFactor) + sourceY)); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = minX; x < maxX; x++) - { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; - } - }); + // X coordinates of source points + targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; + } + }); - // Break out now. - source.SwapPixelsBuffers(targetPixels); - return; - } + return; } // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (var targetPixels = new PixelAccessor(width, height)) + using (var firstPassPixels = new Buffer2D(width, source.Height)) { - using (var firstPassPixels = new Buffer2D(width, source.Height)) - { - firstPassPixels.Clear(); - - Parallel.For( - 0, - sourceRectangle.Bottom, - this.ParallelOptions, - y => + firstPassPixels.Clear(); + + Parallel.For( + 0, + sourceRectangle.Bottom, + configuration.ParallelOptions, + y => + { + // TODO: Without Parallel.For() this buffer object could be reused: + using (var tempRowBuffer = new Buffer(source.Width)) { - // TODO: Without Parallel.For() this buffer object could be reused: - using (var tempRowBuffer = new Buffer(source.Width)) - { - Span firstPassRow = firstPassPixels.GetRowSpan(y); - Span sourceRow = source.GetRowSpan(y); - PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length); + Span firstPassRow = firstPassPixels.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length); - if (this.Compand) + if (this.Compand) + { + for (int x = minX; x < maxX; x++) { - for (int x = minX; x < maxX; x++) - { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); - } + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); } - else + } + else + { + for (int x = minX; x < maxX; x++) { - for (int x = minX; x < maxX; x++) - { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); - } + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); } } - }); - - // Now process the rows. - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - // Ensure offsets are normalised for cropping and padding. - WeightsWindow window = this.VerticalWeights.Weights[y - startY]; - Span targetRow = targetPixels.GetRowSpan(y); + } + }); - if (this.Compand) + // Now process the rows. + Parallel.For( + minY, + maxY, + configuration.ParallelOptions, + y => + { + // Ensure offsets are normalised for cropping and padding. + WeightsWindow window = this.VerticalWeights.Weights[y - startY]; + Span targetRow = cloned.GetPixelRowSpan(y); + + if (this.Compand) + { + for (int x = 0; x < width; x++) { - for (int x = 0; x < width; x++) - { - // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); - destination = destination.Compress(); + // Destination color components + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); + destination = destination.Compress(); - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(destination); - } + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4(destination); } - else + } + else + { + for (int x = 0; x < width; x++) { - for (int x = 0; x < width; x++) - { - // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); + // Destination color components + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(destination); - } + ref TPixel pixel = ref targetRow[x]; + pixel.PackFromVector4(destination); } - }); - } - - source.SwapPixelsBuffers(targetPixels); + } + }); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index d563d072a..a7fb400ac 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -1,17 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the rotating of images. /// @@ -35,9 +35,9 @@ namespace ImageSharp.Processing.Processors public bool Expand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - if (this.OptimizedApply(source)) + if (this.OptimizedApply(source, configuration)) { return; } @@ -45,13 +45,14 @@ namespace ImageSharp.Processing.Processors int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); + Rectangle sourceBounds = source.Bounds(); using (var targetPixels = new PixelAccessor(width, height)) { Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { Span targetRow = targetPixels.GetRowSpan(y); @@ -60,7 +61,7 @@ namespace ImageSharp.Processing.Processors { var transformedPoint = Point.Rotate(new Point(x, y), matrix); - if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetRow[x] = source[transformedPoint.X, transformedPoint.Y]; } @@ -72,7 +73,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon) { @@ -90,8 +91,11 @@ namespace ImageSharp.Processing.Processors /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// /// The source image. - /// The - private bool OptimizedApply(ImageBase source) + /// The configuration. + /// + /// The + /// + private bool OptimizedApply(ImageFrame source, Configuration configuration) { if (MathF.Abs(this.Angle) < Constants.Epsilon) { @@ -101,19 +105,19 @@ namespace ImageSharp.Processing.Processors if (MathF.Abs(this.Angle - 90) < Constants.Epsilon) { - this.Rotate90(source); + this.Rotate90(source, configuration); return true; } if (MathF.Abs(this.Angle - 180) < Constants.Epsilon) { - this.Rotate180(source); + this.Rotate180(source, configuration); return true; } if (MathF.Abs(this.Angle - 270) < Constants.Epsilon) { - this.Rotate270(source); + this.Rotate270(source, configuration); return true; } @@ -124,7 +128,8 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 270 degrees clockwise at the centre point. /// /// The source image. - private void Rotate270(ImageBase source) + /// The configuration. + private void Rotate270(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -136,7 +141,7 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { for (int x = 0; x < width; x++) @@ -157,7 +162,8 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 180 degrees clockwise at the centre point. /// /// The source image. - private void Rotate180(ImageBase source) + /// The configuration. + private void Rotate180(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -167,10 +173,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); Span targetRow = targetPixels.GetRowSpan(height - y - 1); for (int x = 0; x < width; x++) @@ -187,7 +193,8 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 90 degrees clockwise at the centre point. /// /// The source image. - private void Rotate90(ImageBase source) + /// The configuration. + private void Rotate90(ImageFrame source, Configuration configuration) { int width = source.Width; int height = source.Height; @@ -197,10 +204,10 @@ namespace ImageSharp.Processing.Processors Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); + Span sourceRow = source.GetPixelRowSpan(y); int newX = height - y - 1; for (int x = 0; x < width; x++) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 9766caa69..316e2a2af 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -1,17 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing.Processors +{ /// /// Provides methods that allow the skewing of images. /// @@ -40,18 +40,19 @@ namespace ImageSharp.Processing.Processors public bool Expand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); + Rectangle sourceBounds = source.Bounds(); using (var targetPixels = new PixelAccessor(width, height)) { Parallel.For( 0, height, - this.ParallelOptions, + configuration.ParallelOptions, y => { Span targetRow = targetPixels.GetRowSpan(y); @@ -60,7 +61,7 @@ namespace ImageSharp.Processing.Processors { var transformedPoint = Point.Skew(new Point(x, y), matrix); - if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetRow[x] = source[transformedPoint.X, transformedPoint.Y]; } @@ -72,9 +73,9 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - this.processMatrix = Matrix3x2Extensions.CreateSkew(-this.AngleX, -this.AngleY, new Point(0, 0)); + this.processMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0)); if (this.Expand) { this.CreateNewCanvas(sourceRectangle, this.processMatrix); diff --git a/src/ImageSharp/Processing/Transforms/AutoOrient.cs b/src/ImageSharp/Processing/Transforms/AutoOrient.cs index 7e7c66460..b8b31ff27 100644 --- a/src/ImageSharp/Processing/Transforms/AutoOrient.cs +++ b/src/ImageSharp/Processing/Transforms/AutoOrient.cs @@ -1,15 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -21,77 +17,8 @@ namespace ImageSharp /// The pixel format. /// The image to auto rotate. /// The - public static Image AutoOrient(this Image source) + public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) where TPixel : struct, IPixel - { - Orientation orientation = GetExifOrientation(source); - - switch (orientation) - { - case Orientation.TopRight: - return source.Flip(FlipType.Horizontal); - - case Orientation.BottomRight: - return source.Rotate(RotateType.Rotate180); - - case Orientation.BottomLeft: - return source.Flip(FlipType.Vertical); - - case Orientation.LeftTop: - return source.Rotate(RotateType.Rotate90) - .Flip(FlipType.Horizontal); - - case Orientation.RightTop: - return source.Rotate(RotateType.Rotate90); - - case Orientation.RightBottom: - return source.Flip(FlipType.Vertical) - .Rotate(RotateType.Rotate270); - - case Orientation.LeftBottom: - return source.Rotate(RotateType.Rotate270); - - case Orientation.Unknown: - case Orientation.TopLeft: - default: - return source; - } - } - - /// - /// Returns the current EXIF orientation - /// - /// The pixel format. - /// The image to auto rotate. - /// The - private static Orientation GetExifOrientation(Image source) - where TPixel : struct, IPixel - { - if (source.MetaData.ExifProfile == null) - { - return Orientation.Unknown; - } - - ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); - if (value == null) - { - return Orientation.Unknown; - } - - Orientation orientation; - if (value.DataType == ExifDataType.Short) - { - orientation = (Orientation)value.Value; - } - else - { - orientation = (Orientation)Convert.ToUInt16(value.Value); - source.MetaData.ExifProfile.RemoveValue(ExifTag.Orientation); - } - - source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); - - return orientation; - } + => source.ApplyProcessor(new AutoOrientProcessor()); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Crop.cs b/src/ImageSharp/Processing/Transforms/Crop.cs index 94e9ba1f4..3fa59c248 100644 --- a/src/ImageSharp/Processing/Transforms/Crop.cs +++ b/src/ImageSharp/Processing/Transforms/Crop.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -25,11 +21,9 @@ namespace ImageSharp /// The target image width. /// The target image height. /// The - public static Image Crop(this Image source, int width, int height) + public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel - { - return Crop(source, new Rectangle(0, 0, width, height)); - } + => Crop(source, new Rectangle(0, 0, width, height)); /// /// Crops an image to the given rectangle. @@ -40,13 +34,8 @@ namespace ImageSharp /// The structure that specifies the portion of the image object to retain. /// /// The - public static Image Crop(this Image source, Rectangle cropRectangle) + public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) where TPixel : struct, IPixel - { - CropProcessor processor = new CropProcessor(cropRectangle); - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new CropProcessor(cropRectangle)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/EntropyCrop.cs b/src/ImageSharp/Processing/Transforms/EntropyCrop.cs index 2f4a8e383..cbd2b4659 100644 --- a/src/ImageSharp/Processing/Transforms/EntropyCrop.cs +++ b/src/ImageSharp/Processing/Transforms/EntropyCrop.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -23,13 +19,8 @@ namespace ImageSharp /// The image to crop. /// The threshold for entropic density. /// The - public static Image EntropyCrop(this Image source, float threshold = .5f) + public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold = .5f) where TPixel : struct, IPixel - { - EntropyCropProcessor processor = new EntropyCropProcessor(threshold); - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new EntropyCropProcessor(threshold)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Flip.cs b/src/ImageSharp/Processing/Transforms/Flip.cs index 1c8baebf1..e153e89f2 100644 --- a/src/ImageSharp/Processing/Transforms/Flip.cs +++ b/src/ImageSharp/Processing/Transforms/Flip.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,13 +20,8 @@ namespace ImageSharp /// The image to rotate, flip, or both. /// The to perform the flip. /// The - public static Image Flip(this Image source, FlipType flipType) + public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipType flipType) where TPixel : struct, IPixel - { - FlipProcessor processor = new FlipProcessor(flipType); - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new FlipProcessor(flipType)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs b/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs index 67010a777..263af14cc 100644 --- a/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs +++ b/src/ImageSharp/Processing/Transforms/Options/AnchorPosition.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerated anchor positions to apply to resized images. diff --git a/src/ImageSharp/Processing/Transforms/Options/FlipType.cs b/src/ImageSharp/Processing/Transforms/Options/FlipType.cs index 3c66da697..0129891f6 100644 --- a/src/ImageSharp/Processing/Transforms/Options/FlipType.cs +++ b/src/ImageSharp/Processing/Transforms/Options/FlipType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Provides enumeration over how a image should be flipped. diff --git a/src/ImageSharp/Processing/Transforms/Options/Orientation.cs b/src/ImageSharp/Processing/Transforms/Options/Orientation.cs index b8147348f..9c8d96a71 100644 --- a/src/ImageSharp/Processing/Transforms/Options/Orientation.cs +++ b/src/ImageSharp/Processing/Transforms/Options/Orientation.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerates the available orientation values supplied by EXIF metadata. diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs index 7eae89eac..152b451e2 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing -{ - using System.Linq; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; +using System.Linq; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing +{ /// /// Provides methods to help calculate the target rectangle when resizing using the /// enumeration. @@ -25,7 +22,7 @@ namespace ImageSharp.Processing /// /// The . /// - public static Rectangle CalculateTargetLocationAndBounds(ImageBase source, ResizeOptions options) + public static Rectangle CalculateTargetLocationAndBounds(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { switch (options.Mode) @@ -56,7 +53,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateCropRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateCropRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -175,7 +172,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculatePadRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculatePadRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -256,7 +253,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateBoxPadRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateBoxPadRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -343,7 +340,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateMaxRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateMaxRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; @@ -384,7 +381,7 @@ namespace ImageSharp.Processing /// /// The . /// - private static Rectangle CalculateMinRectangle(ImageBase source, ResizeOptions options) + private static Rectangle CalculateMinRectangle(ImageFrame source, ResizeOptions options) where TPixel : struct, IPixel { int width = options.Size.Width; diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs index dc6092de8..c88808f75 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeMode.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Enumerated resize modes to apply to resized images. diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs index 2ca2b1c39..8f2d3db0a 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing -{ - using System.Collections.Generic; - using System.Linq; - using SixLabors.Primitives; +using System.Collections.Generic; +using System.Linq; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp.Processing +{ /// /// The resize options for resizing images against certain modes. /// diff --git a/src/ImageSharp/Processing/Transforms/Options/RotateType.cs b/src/ImageSharp/Processing/Transforms/Options/RotateType.cs index 9f09cec28..9f6d45f2b 100644 --- a/src/ImageSharp/Processing/Transforms/Options/RotateType.cs +++ b/src/ImageSharp/Processing/Transforms/Options/RotateType.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Provides enumeration over how the image should be rotated. diff --git a/src/ImageSharp/Processing/Transforms/Pad.cs b/src/ImageSharp/Processing/Transforms/Pad.cs index 8138fd765..eb0f2e9c2 100644 --- a/src/ImageSharp/Processing/Transforms/Pad.cs +++ b/src/ImageSharp/Processing/Transforms/Pad.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -26,7 +22,7 @@ namespace ImageSharp /// The new width. /// The new height. /// The . - public static Image Pad(this Image source, int width, int height) + public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel { ResizeOptions options = new ResizeOptions diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs index e40bf59f9..be9de9eda 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the bicubic kernel algorithm W(x) as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs index f174ef48b..5aab0d07f 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the box algorithm. Similar to nearest neighbor when upscaling. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs index 0725ecb53..1c8467618 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The Catmull-Rom filter is a well known standard Cubic Filter often used as a interpolation function. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs index 66cc36a21..33435059f 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The Hermite filter is type of smoothed triangular interpolation Filter, diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs index 6339d9d8e..9a128a05b 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/IResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// Encapsulates an interpolation algorithm for resampling images. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs index a46f38df4..deaa0ccb9 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs index 3ad01b2b7..2673c3491 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs index 9bb8bdd16..c52670e2d 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs index af8c2c111..552d3065b 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Lanczos kernel algorithm as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs index 38ca614c4..df351d950 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the mitchell algorithm as described on diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs index f1e5ab8fd..7a7785be3 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the nearest neighbor algorithm. This uses an unscaled filter diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs index 1b060e6e3..bd28d8da4 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Robidoux algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs index 86c7b0517..a345da3f4 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the Robidoux Sharp algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs index abd5b835c..ac5e2dedb 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the spline algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs index bf88b65df..842da87e0 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the triangle (bilinear) algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs index 33a2cc825..e154d5483 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Processing +namespace SixLabors.ImageSharp.Processing { /// /// The function implements the welch algorithm. diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index e00faf10e..826741d94 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; - using SixLabors.Primitives; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,41 +21,45 @@ namespace ImageSharp /// The resize options. /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, ResizeOptions options) + public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) where TPixel : struct, IPixel { - // Ensure size is populated across both dimensions. - if (options.Size.Width == 0 && options.Size.Height > 0) + return source.Apply(img => { - options.Size = new Size((int)MathF.Round(source.Width * options.Size.Height / (float)source.Height), options.Size.Height); - } - - if (options.Size.Height == 0 && options.Size.Width > 0) - { - options.Size = new Size(options.Size.Width, (int)MathF.Round(source.Height * options.Size.Width / (float)source.Width)); - } - - Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(source, options); - - return Resize(source, options.Size.Width, options.Size.Height, options.Sampler, source.Bounds, targetRectangle, options.Compand); + // Cheat and bound through a run, inside here we should just be mutating, this really needs moving over to a processor + // Ensure size is populated across both dimensions. + if (options.Size.Width == 0 && options.Size.Height > 0) + { + options.Size = new Size((int)MathF.Round(img.Width * options.Size.Height / (float)img.Height), options.Size.Height); + } + + if (options.Size.Height == 0 && options.Size.Width > 0) + { + options.Size = new Size(options.Size.Width, (int)MathF.Round(img.Height * options.Size.Width / (float)img.Width)); + } + + Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(img, options); + + img.Mutate(x => Resize(x, options.Size.Width, options.Size.Height, options.Sampler, targetRectangle, options.Compand)); + }); } /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. /// The target image size. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, Size size) + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) where TPixel : struct, IPixel { return Resize(source, size.Width, size.Height, new BicubicResampler(), false); } /// - /// Resizes an image to the given . + /// Resizes an image to the given . /// /// The pixel format. /// The image to resize. @@ -66,7 +67,7 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, Size size, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) where TPixel : struct, IPixel { return Resize(source, size.Width, size.Height, new BicubicResampler(), compand); @@ -81,7 +82,7 @@ namespace ImageSharp /// The target image height. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel { return Resize(source, width, height, new BicubicResampler(), false); @@ -97,7 +98,7 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) where TPixel : struct, IPixel { return Resize(source, width, height, new BicubicResampler(), compand); @@ -113,12 +114,28 @@ namespace ImageSharp /// The to perform the resampling. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) where TPixel : struct, IPixel { return Resize(source, width, height, sampler, false); } + /// + /// Resizes an image to the given width and height with the given sampler. + /// + /// The pixel format. + /// The image to resize. + /// The target image size. + /// The to perform the resampling. + /// Whether to compress and expand the image color-space to gamma correct the image during processing. + /// The + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) + where TPixel : struct, IPixel + { + return Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); + } + /// /// Resizes an image to the given width and height with the given sampler. /// @@ -130,10 +147,10 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) where TPixel : struct, IPixel { - return Resize(source, width, height, sampler, source.Bounds, new Rectangle(0, 0, width, height), compand); + return Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); } /// @@ -154,28 +171,69 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) where TPixel : struct, IPixel { - if (width == 0 && height > 0) + return source.Apply(img => { - width = (int)MathF.Round(source.Width * height / (float)source.Height); - targetRectangle.Width = width; - } + // TODO : Stop cheating here and move this stuff into the processors itself + if (width == 0 && height > 0) + { + width = (int)MathF.Round(img.Width * height / (float)img.Height); + targetRectangle.Width = width; + } + + if (height == 0 && width > 0) + { + height = (int)MathF.Round(img.Height * width / (float)img.Width); + targetRectangle.Height = height; + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }, sourceRectangle)); + }); + } - if (height == 0 && width > 0) + /// + /// Resizes an image to the given width and height with the given sampler and + /// source rectangle. + /// + /// The pixel format. + /// The image to resize. + /// The target image width. + /// The target image height. + /// The to perform the resampling. + /// + /// The structure that specifies the portion of the target image object to draw to. + /// + /// Whether to compress and expand the image color-space to gamma correct the image during processing. + /// The + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle targetRectangle, bool compand) + where TPixel : struct, IPixel + { + return source.Apply(img => { - height = (int)MathF.Round(source.Height * width / (float)source.Width); - targetRectangle.Height = height; - } - - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - - var processor = new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }; - - source.ApplyProcessor(processor, sourceRectangle); - return source; + // TODO : stop cheating here and move this stuff into the processors itself + if (width == 0 && height > 0) + { + width = (int)MathF.Round(img.Width * height / (float)img.Height); + targetRectangle.Width = width; + } + + if (height == 0 && width > 0) + { + height = (int)MathF.Round(img.Height * width / (float)img.Width); + targetRectangle.Height = height; + } + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + img.Mutate(x => x.ApplyProcessor(new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand })); + }); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index af7c06a27..7b35a879b 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; - using Processing.Processors; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The image to rotate. /// The angle in degrees to perform the rotation. /// The - public static Image Rotate(this Image source, float degrees) + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) where TPixel : struct, IPixel { return Rotate(source, degrees, true); @@ -37,11 +33,9 @@ namespace ImageSharp /// The image to rotate. /// The to perform the rotation. /// The - public static Image Rotate(this Image source, RotateType rotateType) + public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateType rotateType) where TPixel : struct, IPixel - { - return Rotate(source, (float)rotateType, false); - } + => Rotate(source, (float)rotateType, false); /// /// Rotates an image by the given angle in degrees. @@ -51,13 +45,8 @@ namespace ImageSharp /// The angle in degrees to perform the rotation. /// Whether to expand the image to fit the rotated result. /// The - public static Image Rotate(this Image source, float degrees, bool expand) + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, bool expand) where TPixel : struct, IPixel - { - RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new RotateProcessor { Angle = degrees, Expand = expand }); } } diff --git a/src/ImageSharp/Processing/Transforms/RotateFlip.cs b/src/ImageSharp/Processing/Transforms/RotateFlip.cs index 805deb8d1..2ddcb151b 100644 --- a/src/ImageSharp/Processing/Transforms/RotateFlip.cs +++ b/src/ImageSharp/Processing/Transforms/RotateFlip.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using ImageSharp.Processing; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The to perform the rotation. /// The to perform the flip. /// The - public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType) + public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateType rotateType, FlipType flipType) where TPixel : struct, IPixel { return source.Rotate(rotateType).Flip(flipType); diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index d42d79225..00411946d 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -1,16 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - - using ImageSharp.PixelFormats; - - using Processing.Processors; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +20,7 @@ namespace ImageSharp /// The angle in degrees to perform the rotation along the x-axis. /// The angle in degrees to perform the rotation along the y-axis. /// The - public static Image Skew(this Image source, float degreesX, float degreesY) + public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) where TPixel : struct, IPixel { return Skew(source, degreesX, degreesY, true); @@ -39,13 +35,8 @@ namespace ImageSharp /// The angle in degrees to perform the rotation along the y-axis. /// Whether to expand the image to fit the skewed result. /// The - public static Image Skew(this Image source, float degreesX, float degreesY, bool expand) + public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, bool expand) where TPixel : struct, IPixel - { - SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; - - source.ApplyProcessor(processor, source.Bounds); - return source; - } + => source.ApplyProcessor(new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }); } } diff --git a/src/ImageSharp/Properties/AssemblyInfo.cs b/src/ImageSharp/Properties/AssemblyInfo.cs index e791dff5a..7b8f933b0 100644 --- a/src/ImageSharp/Properties/AssemblyInfo.cs +++ b/src/ImageSharp/Properties/AssemblyInfo.cs @@ -1,8 +1,6 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Runtime.CompilerServices; // Ensure the other projects can see the internal helpers -[assembly: InternalsVisibleTo("ImageSharp.Drawing")] \ No newline at end of file +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Box.cs b/src/ImageSharp/Quantizers/Box.cs index 7f2a32087..4a1e17753 100644 --- a/src/ImageSharp/Quantizers/Box.cs +++ b/src/ImageSharp/Quantizers/Box.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers +namespace SixLabors.ImageSharp.Quantizers { /// /// Represents a box color cube. diff --git a/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs index b7cf2d469..82f6146a3 100644 --- a/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Provides methods for for allowing quantization of images pixels with configurable dithering. /// @@ -23,7 +21,7 @@ namespace ImageSharp.Quantizers /// /// A representing a quantized version of the image pixels. /// - QuantizedImage Quantize(ImageBase image, int maxColors); + QuantizedImage Quantize(ImageFrame image, int maxColors); } /// diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index 8ebea8533..49adce23b 100644 --- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -1,21 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers.Base; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Encapsulates methods to calculate the color palette if an image using an Octree pattern. /// /// /// The pixel format. - public sealed class OctreeQuantizer : Quantizer + public sealed class OctreeQuantizer : QuantizerBase where TPixel : struct, IPixel { /// @@ -56,7 +56,7 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { this.colors = (byte)maxColors.Clamp(1, 255); this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors)); @@ -66,7 +66,7 @@ namespace ImageSharp.Quantizers } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. @@ -78,7 +78,7 @@ namespace ImageSharp.Quantizers for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -105,7 +105,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index d121dc6ae..ca11a042f 100644 --- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -1,21 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers.Base; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Encapsulates methods to create a quantized image based upon the given palette. /// /// /// The pixel format. - public sealed class PaletteQuantizer : Quantizer + public sealed class PaletteQuantizer : QuantizerBase where TPixel : struct, IPixel { /// @@ -55,14 +55,14 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); return base.Quantize(image, maxColors); } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. @@ -74,7 +74,7 @@ namespace ImageSharp.Quantizers for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -101,7 +101,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/ImageSharp/Quantizers/Quantization.cs b/src/ImageSharp/Quantizers/Quantization.cs index 039404384..df55d3e87 100644 --- a/src/ImageSharp/Quantizers/Quantization.cs +++ b/src/ImageSharp/Quantizers/Quantization.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp +namespace SixLabors.ImageSharp { /// /// Provides enumeration over how an image should be quantized. diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index a23509278..049090f43 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp -{ - using System; - using System.Threading.Tasks; - - using ImageSharp.PixelFormats; - using ImageSharp.Quantizers; +using System; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers; +namespace SixLabors.ImageSharp +{ /// /// Extension methods for the type. /// @@ -24,7 +22,7 @@ namespace ImageSharp /// The quantization mode to apply to perform the operation. /// The maximum number of colors to return. Defaults to 256. /// The . - public static Image Quantize(this Image source, Quantization mode = Quantization.Octree, int maxColors = 256) + public static IImageProcessingContext Quantize(this IImageProcessingContext source, Quantization mode = Quantization.Octree, int maxColors = 256) where TPixel : struct, IPixel { IQuantizer quantizer; @@ -54,31 +52,34 @@ namespace ImageSharp /// The quantizer to apply to perform the operation. /// The maximum number of colors to return. /// The . - public static Image Quantize(this Image source, IQuantizer quantizer, int maxColors) + public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer, int maxColors) where TPixel : struct, IPixel { - QuantizedImage quantized = quantizer.Quantize(source, maxColors); - int palleteCount = quantized.Palette.Length - 1; - - using (PixelAccessor pixels = new PixelAccessor(quantized.Width, quantized.Height)) + return source.Apply(img => { - Parallel.For( - 0, - pixels.Height, - source.Configuration.ParallelOptions, - y => - { - for (int x = 0; x < pixels.Width; x++) + // TODO : move helper logic into the processor + QuantizedImage quantized = quantizer.Quantize(img, maxColors); + int palleteCount = quantized.Palette.Length - 1; + + using (var pixels = new PixelAccessor(quantized.Width, quantized.Height)) + { + Parallel.For( + 0, + pixels.Height, + img.GetConfiguration().ParallelOptions, + y => { - int i = x + (y * pixels.Width); - TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; - pixels[x, y] = color; - } - }); + for (int x = 0; x < pixels.Width; x++) + { + int i = x + (y * pixels.Width); + TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; + pixels[x, y] = color; + } + }); - source.SwapPixelsBuffers(pixels); - return source; - } + img.Frames[0].SwapPixelsBuffers(pixels); + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs index c8b2c7df6..52cf1a8d9 100644 --- a/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Represents a quantized image where the pixels indexed by a color palette. /// diff --git a/src/ImageSharp/Quantizers/Quantizer{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs similarity index 86% rename from src/ImageSharp/Quantizers/Quantizer{TPixel}.cs rename to src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs index 2e3ea7a54..7f58ff1bf 100644 --- a/src/ImageSharp/Quantizers/Quantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs @@ -1,22 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Collections.Generic; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.Dithering; - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Quantizers.Base +{ /// /// Encapsulates methods to calculate the color palette of an image. /// /// The pixel format. - public abstract class Quantizer : IQuantizer + public abstract class QuantizerBase : IQuantizer where TPixel : struct, IPixel { /// @@ -25,7 +24,7 @@ namespace ImageSharp.Quantizers private readonly bool singlePass; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// If true, the quantization only needs to loop through the source pixels once @@ -35,7 +34,7 @@ namespace ImageSharp.Quantizers /// only call the 'QuantizeImage' function. If two passes are required, the code will call 'InitialQuantizeImage' /// and then 'QuantizeImage'. /// - protected Quantizer(bool singlePass) + protected QuantizerBase(bool singlePass) { this.singlePass = singlePass; } @@ -44,10 +43,10 @@ namespace ImageSharp.Quantizers public bool Dither { get; set; } = true; /// - public IErrorDiffuser DitherType { get; set; } = new SierraLite(); + public IErrorDiffuser DitherType { get; set; } = new SierraLiteDiffuser(); /// - public virtual QuantizedImage Quantize(ImageBase image, int maxColors) + public virtual QuantizedImage Quantize(ImageFrame image, int maxColors) { Guard.NotNull(image, nameof(image)); @@ -70,7 +69,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // We clone the image as we don't want to alter the original. - using (var clone = new Image(image)) + using (ImageFrame clone = image.Clone()) { this.SecondPass(clone, quantizedPixels, width, height); } @@ -89,12 +88,12 @@ namespace ImageSharp.Quantizers /// The source data /// The width in pixels of the image. /// The height in pixels of the image. - protected virtual void FirstPass(ImageBase source, int width, int height) + protected virtual void FirstPass(ImageFrame source, int width, int height) { // Loop through each row for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -112,7 +111,7 @@ namespace ImageSharp.Quantizers /// The output pixel array /// The width in pixels of the image /// The height in pixels of the image - protected abstract void SecondPass(ImageBase source, byte[] output, int width, int height); + protected abstract void SecondPass(ImageFrame source, byte[] output, int width, int height); /// /// Override this to process the pixel in the first pass of the algorithm diff --git a/src/ImageSharp/Quantizers/WuArrayPool.cs b/src/ImageSharp/Quantizers/WuArrayPool.cs index bd8ee9d6b..04a70637b 100644 --- a/src/ImageSharp/Quantizers/WuArrayPool.cs +++ b/src/ImageSharp/Quantizers/WuArrayPool.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System.Buffers; +using System.Buffers; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// Provides array pooling for the . /// This is a separate class so that the pools can be shared accross multiple generic quantizer instaces. diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index aecca771c..77c421468 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -1,17 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Quantizers -{ - using System; - using System.Buffers; - using System.Collections.Generic; - using System.Numerics; - using System.Runtime.CompilerServices; - using ImageSharp.PixelFormats; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Quantizers.Base; +namespace SixLabors.ImageSharp.Quantizers +{ /// /// An implementation of Wu's color quantizer with alpha channel. /// @@ -32,7 +32,7 @@ namespace ImageSharp.Quantizers /// /// /// The pixel format. - public class WuQuantizer : Quantizer + public class WuQuantizer : QuantizerBase where TPixel : struct, IPixel { /// @@ -133,7 +133,7 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageFrame image, int maxColors) { Guard.NotNull(image, nameof(image)); @@ -221,7 +221,7 @@ namespace ImageSharp.Quantizers } /// - protected override void FirstPass(ImageBase source, int width, int height) + protected override void FirstPass(ImageFrame source, int width, int height) { // Build up the 3-D color histogram // Loop through each row @@ -240,7 +240,7 @@ namespace ImageSharp.Quantizers } /// - protected override void SecondPass(ImageBase source, byte[] output, int width, int height) + protected override void SecondPass(ImageFrame source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. @@ -252,7 +252,7 @@ namespace ImageSharp.Quantizers for (int y = 0; y < height; y++) { - Span row = source.GetRowSpan(y); + Span row = source.GetPixelRowSpan(y); // And loop through each column for (int x = 0; x < width; x++) @@ -279,7 +279,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, 0, 0, width, height, false); } output[(y * source.Width) + x] = pixelValue; diff --git a/src/Shared/AssemblyInfo.Common.cs b/src/Shared/AssemblyInfo.Common.cs index 252ef3eae..327d3abd7 100644 --- a/src/Shared/AssemblyInfo.Common.cs +++ b/src/Shared/AssemblyInfo.Common.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Reflection; using System.Resources; @@ -12,9 +10,9 @@ using System.Runtime.CompilerServices; // associated with an assembly. [assembly: AssemblyDescription("A cross-platform library for processing of image files; written in C#")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("James Jackson-South")] -[assembly: AssemblyProduct("ImageSharp")] -[assembly: AssemblyCopyright("Copyright (c) James Jackson-South and contributors.")] +[assembly: AssemblyCompany("Six Labors")] +[assembly: AssemblyProduct("SixLabors.ImageSharp")] +[assembly: AssemblyCopyright("Copyright (c) Six Labors and contributors.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] @@ -34,7 +32,10 @@ using System.Runtime.CompilerServices; [assembly: AssemblyInformationalVersion("1.0.0.0")] // Ensure the internals can be built and tested. -[assembly: InternalsVisibleTo("ImageSharp.Drawing")] +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] [assembly: InternalsVisibleTo("ImageSharp.Benchmarks")] -[assembly: InternalsVisibleTo("ImageSharp.Tests")] -[assembly: InternalsVisibleTo("ImageSharp.Sandbox46")] \ No newline at end of file +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Tests")] +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Sandbox46")] + +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKeyToken=null")] diff --git a/src/Shared/stylecop.json b/src/Shared/stylecop.json deleted file mode 100644 index df8f120a5..000000000 --- a/src/Shared/stylecop.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "James Jackson-South", - "copyrightText": "Copyright (c) James Jackson-South and contributors.\nLicensed under the Apache License, Version 2.0." - } - } -} \ No newline at end of file diff --git a/stylecop.json b/stylecop.json new file mode 100644 index 000000000..485ab604a --- /dev/null +++ b/stylecop.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace", + "elementOrder": [ + "kind" + ] + }, + "documentationRules": { + "xmlHeader": false, + "documentInternalElements": false, + "copyrightText": "Copyright (c) Six Labors and contributors.\nLicensed under the Apache License, Version 2.0." + } + } +} \ No newline at end of file diff --git a/tests/CodeCoverage/CodeCoverage.cmd b/tests/CodeCoverage/CodeCoverage.cmd index 1e16d5c14..2c13cfc14 100644 --- a/tests/CodeCoverage/CodeCoverage.cmd +++ b/tests/CodeCoverage/CodeCoverage.cmd @@ -9,9 +9,8 @@ cd .. cd .. dotnet restore ImageSharp.sln -dotnet build ImageSharp.sln --no-incremental -c release /p:codecov=true rem The -threshold options prevents this taking ages... -tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj --no-build -c release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[ImageSharp*]*" +tests\CodeCoverage\OpenCover.4.6.519\tools\OpenCover.Console.exe -target:"dotnet.exe" -targetargs:"test tests\ImageSharp.Tests\ImageSharp.Tests.csproj -c Release /p:codecov=true" -register:user -threshold:10 -oldStyle -safemode:off -output:.\ImageSharp.Coverage.xml -hideskipped:All -returntargetcode -filter:"+[SixLabors.ImageSharp*]*" if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs index 41574d109..6db03a448 100644 --- a/tests/ImageSharp.Benchmarks/BenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/BenchmarkBase.cs @@ -1,6 +1,6 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; /// /// The image benchmark base class. diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs new file mode 100644 index 000000000..e88981959 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs @@ -0,0 +1,65 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; + + [Config(typeof(Config.Short))] + public abstract class PackFromVector4 + where TPixel : struct, IPixel + { + private Buffer source; + + private Buffer destination; + + [Params(16, 128, 512)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count); + } + + [GlobalCleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } + + [Benchmark(Baseline = true)] + public void PerElement() + { + Vector4[] s = this.source.Array; + TPixel[] d = this.destination.Array; + + for (int i = 0; i < this.Count; i++) + { + d[i].PackFromVector4(s[i]); + } + } + + [Benchmark] + public void CommonBulk() + { + new PixelOperations().PackFromVector4(this.source, this.destination, this.Count); + } + + [Benchmark] + public void OptimizedBulk() + { + PixelOperations.Instance.PackFromVector4(this.source, this.destination, this.Count); + } + } + + public class PackFromVector4_Rgba32 : PackFromVector4 + { + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index 78bdfd159..b4f6ea9c0 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp; - using ImageSharp.Memory; + using SixLabors.ImageSharp; + using SixLabors.ImageSharp.Memory; /// /// Compares two implementation candidates for general BulkPixelOperations.ToVector4(): diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 7b151989e..5c3648c2d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -1,10 +1,10 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class PackFromXyzw where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 9976faa8c..2bf4e0da6 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -1,12 +1,12 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using System.Numerics; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class ToVector4 where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 061a5bd37..e1f5e6106 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -1,10 +1,10 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class ToXyz where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index f4bcce115..603289763 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -4,12 +4,12 @@ using System.Linq; using System.Threading.Tasks; // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Color.Bulk +namespace SixLabors.ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats; public abstract class ToXyzw where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index a641baafe..02017cbb7 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -3,11 +3,11 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using SystemColor = System.Drawing.Color; diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs index b29fdc25b..c5792f547 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToCieLabConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs index 3e7d60972..7528f75f8 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToHunterLabConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index f472dd292..a4da78090 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToLmsConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index eeea86c6e..dab0e7a51 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class ColorspaceCieXyzToRgbConvert { diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs index 18c8cb372..335ecf478 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.LookupTables.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { public partial class RgbToYCbCr { diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs index 2efd9bc42..4048b2569 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs @@ -1,14 +1,16 @@ -namespace ImageSharp.Benchmarks -{ - using System; - using System.Buffers; - using System.Numerics; - using System.Runtime.CompilerServices; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using BenchmarkDotNet.Attributes; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +namespace SixLabors.ImageSharp.Benchmarks +{ public partial class RgbToYCbCr { private const int InputColorCount = 64; diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index 21d040e5c..f4e0fd65f 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -1,12 +1,12 @@ -namespace ImageSharp.Benchmarks.Color +namespace SixLabors.ImageSharp.Benchmarks.Color { using BenchmarkDotNet.Attributes; using Colourful; using Colourful.Conversion; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; + using SixLabors.ImageSharp.ColorSpaces; + using SixLabors.ImageSharp.ColorSpaces.Conversion; public class RgbWorkingSpaceAdapt { diff --git a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs index dad32626d..2e3307d29 100644 --- a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs +++ b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 8e278330b..9577bebd3 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -5,7 +5,7 @@ using BenchmarkDotNet.Configs; -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Jobs; @@ -22,7 +22,7 @@ namespace ImageSharp.Benchmarks public Short() { this.Add( - Job.Default.WithLaunchCount(1) + Job.Clr.WithLaunchCount(1) .WithWarmupCount(3) .WithTargetCount(3) ); diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index 66104944e..ce1a88599 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -12,7 +12,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class DrawBeziers : BenchmarkBase { @@ -47,7 +47,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.DrawBeziers( + image.Mutate(x => x.DrawBeziers( Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { @@ -55,7 +55,7 @@ namespace ImageSharp.Benchmarks new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 500) - }); + })); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index a6af5a976..4f40c001d 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -12,7 +12,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class DrawLines : BenchmarkBase { @@ -45,14 +45,14 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.DrawLines( + image.Mutate(x => x.DrawLines( Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(550, 50), new Vector2(200, 400) - }); + })); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 3abd3b889..fd8e4ad28 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -13,7 +13,7 @@ namespace ImageSharp.Benchmarks using System.IO; using System.Numerics; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class DrawPolygon : BenchmarkBase { @@ -47,14 +47,14 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.DrawPolygon( + image.Mutate(x => x.DrawPolygon( Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(550, 50), new Vector2(200, 400) - }); + })); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs index b7d1b96b7..f948c4921 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -13,7 +13,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class FillPolygon : BenchmarkBase { @@ -55,13 +55,13 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.FillPolygon( + image.Mutate(x => x.FillPolygon( Rgba32.HotPink, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(550, 50), new Vector2(200, 400) - }); + })); using (MemoryStream ms = new MemoryStream()) { @@ -75,9 +75,9 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Fill( + image.Mutate(x => x.Fill( Rgba32.HotPink, - this.shape); + this.shape)); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index 0738812a1..b3890c101 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -14,7 +14,7 @@ namespace ImageSharp.Benchmarks using System.Numerics; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class FillRectangle : BenchmarkBase { @@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140)); + image.Mutate(x => x.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140))); return new CoreSize(image.Width, image.Height); } @@ -50,13 +50,13 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.FillPolygon( + image.Mutate(x => x.FillPolygon( Rgba32.HotPink, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 10), new Vector2(200, 150), - new Vector2(10, 150) }); + new Vector2(10, 150) })); return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index aa97efe00..46d02e419 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; @@ -11,9 +11,9 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using ImageSharp.Drawing.Brushes; + using SixLabors.ImageSharp.Drawing.Brushes; using CoreBrushes = ImageSharp.Drawing.Brushes.Brushes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; public class FillWithPattern { @@ -40,7 +40,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink)); + image.Mutate(x => x.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink))); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/General/Abs.cs b/tests/ImageSharp.Benchmarks/General/Abs.cs index b5b449467..a67f3f107 100644 --- a/tests/ImageSharp.Benchmarks/General/Abs.cs +++ b/tests/ImageSharp.Benchmarks/General/Abs.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index b7ac2cd86..02df1a19e 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; public class Array2D { diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index dd0ebd413..ddfa0f08b 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -2,7 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; diff --git a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs index 27341b146..45a8519a9 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; diff --git a/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs b/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs similarity index 84% rename from tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs rename to tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs index f99829034..bad87cc11 100644 --- a/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs +++ b/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs @@ -1,19 +1,22 @@ -namespace ImageSharp.Benchmarks.General -{ - using System.Numerics; - using System.Runtime.CompilerServices; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using BenchmarkDotNet.Attributes; +using System.Numerics; +using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg.Common; +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Benchmarks.General +{ /// /// The goal of this benchmark is to measure the following Jpeg-related scenario: /// - Take 2 blocks of float-s /// - Divide each float pair, round the result /// - Iterate through all rounded values as int-s /// - public unsafe class RoundSinglePrecisionBlocks + public unsafe class Block8x8F_DivideRound { private const int ExecutionCount = 5; // Added this to reduce the effect of copying the blocks private static readonly Vector4 MinusOne = new Vector4(-1); @@ -25,7 +28,7 @@ [GlobalSetup] public void Setup() { - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { this.inputDividend[i] = i*44.8f; this.inputDivisior[i] = 100 - i; @@ -42,18 +45,18 @@ float* pDividend = (float*)&b1; float* pDivisor = (float*)&b2; - int* result = stackalloc int[Block8x8F.ScalarCount]; + int* result = stackalloc int[Block8x8F.Size]; for (int cnt = 0; cnt < ExecutionCount; cnt++) { sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { int a = (int) pDividend[i]; int b = (int) pDivisor; result[i] = RationalRound(a, b); } - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += result[i]; } @@ -75,12 +78,12 @@ for (int cnt = 0; cnt < ExecutionCount; cnt++) { sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { double value = pDividend[i] / pDivisor[i]; pDividend[i] = (float) System.Math.Round(value); } - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += (int) pDividend[i]; } @@ -101,7 +104,7 @@ { sum = 0; DivideRoundAll(ref bDividend, ref bDivisor); - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += (int)pDividend[i]; } diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs new file mode 100644 index 000000000..2739877a6 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs @@ -0,0 +1,67 @@ +// ReSharper disable InconsistentNaming + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg.Common; + +namespace SixLabors.ImageSharp.Benchmarks.General +{ + public class Block8x8F_Round + { + private Block8x8F block = default(Block8x8F); + + [GlobalSetup] + public void Setup() + { + if (Vector.Count != 8) + { + throw new NotSupportedException("Vector.Count != 8"); + } + + for (int i = 0; i < Block8x8F.Size; i++) + { + this.block[i] = i * 44.8f; + } + } + + [Benchmark(Baseline = true)] + public void ScalarRound() + { + ref float b = ref Unsafe.As(ref this.block); + + for (int i = 0; i < Block8x8F.Size; i++) + { + ref float v = ref Unsafe.Add(ref b, i); + v = MathF.Round(v); + } + } + + [Benchmark] + public void SimdRound() + { + ref Block8x8F b = ref this.block; + + ref Vector row0 = ref Unsafe.As>(ref b.V0L); + row0 = SimdUtils.FastRound(row0); + ref Vector row1 = ref Unsafe.As>(ref b.V1L); + row1 = SimdUtils.FastRound(row1); + ref Vector row2 = ref Unsafe.As>(ref b.V2L); + row2 = SimdUtils.FastRound(row2); + ref Vector row3 = ref Unsafe.As>(ref b.V3L); + row3 = SimdUtils.FastRound(row3); + ref Vector row4 = ref Unsafe.As>(ref b.V4L); + row4 = SimdUtils.FastRound(row4); + ref Vector row5 = ref Unsafe.As>(ref b.V5L); + row5 = SimdUtils.FastRound(row5); + ref Vector row6 = ref Unsafe.As>(ref b.V6L); + row6 = SimdUtils.FastRound(row6); + ref Vector row7 = ref Unsafe.As>(ref b.V7L); + row7 = SimdUtils.FastRound(row7); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Clamp.cs b/tests/ImageSharp.Benchmarks/General/Clamp.cs index ae53de9d3..ef6bc3c40 100644 --- a/tests/ImageSharp.Benchmarks/General/Clamp.cs +++ b/tests/ImageSharp.Benchmarks/General/Clamp.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index c3a309bb8..0ac1413be 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -1,12 +1,12 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; public unsafe class ClearBuffer { diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index cc8d99b41..48ee266fe 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -1,11 +1,11 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; public class IterateArray { diff --git a/tests/ImageSharp.Benchmarks/General/Modulus.cs b/tests/ImageSharp.Benchmarks/General/Modulus.cs index aa3e461e1..e6d5ccce6 100644 --- a/tests/ImageSharp.Benchmarks/General/Modulus.cs +++ b/tests/ImageSharp.Benchmarks/General/Modulus.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using BenchmarkDotNet.Attributes; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs index 0f025c9a4..a1db525a6 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs index 721ac121a..5b059e2e6 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Numerics; using System.Runtime.CompilerServices; @@ -108,7 +108,7 @@ namespace ImageSharp.Benchmarks.General [Params(32)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.nonVectorRunner = new ConversionRunner(this.Count); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs index 0c9a8af3f..782c79239 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs @@ -1,5 +1,5 @@ // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -111,7 +111,7 @@ namespace ImageSharp.Benchmarks.General [Params(128)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index bd2bf599c..0e21caffb 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -1,11 +1,11 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; // Pixel indexing benchmarks compare different methods for getting/setting all pixel values in a subsegment of a single pixel row. public abstract unsafe class PixelIndexing diff --git a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs index 820789ee7..ae11806f3 100644 --- a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs +++ b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.General { using System; using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs index b882403c8..d189411b5 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs index caadaaa34..637846747 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs index 6b3edb192..49ada2f36 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs new file mode 100644 index 000000000..b38429557 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Divide.cs @@ -0,0 +1,69 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System; + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class DivFloat : SIMDBenchmarkBase.Divide + { + protected override float GetTestValue() => 42; + + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + } + + public class Divide : SIMDBenchmarkBase.Divide + { + protected override uint GetTestValue() => 42; + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + } + + public class DivInt32 : SIMDBenchmarkBase.Divide + { + protected override int GetTestValue() => 42; + + [Benchmark(Baseline = true)] + public void Standard() + { + int v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + } + + public class DivInt16 : SIMDBenchmarkBase.Divide + { + protected override short GetTestValue() => 42; + + protected override Vector GetTestVector() => new Vector(new short[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}); + + [Benchmark(Baseline = true)] + public void Standard() + { + short v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = (short)(this.input[i] / v); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index e5ae49b2a..8c5f56818 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs index 532acc02d..913d4ce3c 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs new file mode 100644 index 000000000..d1b70f21b --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Multiply.cs @@ -0,0 +1,50 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + using BenchmarkDotNet.Attributes; + + public class MulUInt32 : SIMDBenchmarkBase.Multiply + { + protected override uint GetTestValue() => 42u; + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] * v; + } + } + } + + public class MulInt32 : SIMDBenchmarkBase.Multiply + { + [Benchmark(Baseline = true)] + public void Standard() + { + int v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] * v; + } + } + } + + public class MulInt16 : SIMDBenchmarkBase.Multiply + { + protected override short GetTestValue() => 42; + + protected override Vector GetTestVector() => new Vector(new short[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }); + + [Benchmark(Baseline = true)] + public void Standard() + { + short v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = (short)(this.input[i] * v); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs index 30443fad9..f3853a8b1 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System.Numerics; using System.Runtime.InteropServices; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs new file mode 100644 index 000000000..76987dbd2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/SIMDBenchmarkBase.cs @@ -0,0 +1,70 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + using BenchmarkDotNet.Attributes; + + public abstract class SIMDBenchmarkBase + where T : struct + { + protected T[] input; + + protected T[] result; + + protected T testValue; + + protected Vector testVector; + + protected virtual T GetTestValue() => default(T); + + protected virtual Vector GetTestVector() => new Vector(this.GetTestValue()); + + + [Params(32)] + public int InputSize { get; set; } + + [GlobalSetup] + public virtual void Setup() + { + this.input = new T[this.InputSize]; + this.result = new T[this.InputSize]; + this.testValue = this.GetTestValue(); + this.testVector = this.GetTestVector(); + } + + public abstract class Multiply : SIMDBenchmarkBase + { + [Benchmark] + public void Simd() + { + Vector v = this.testVector; + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = Unsafe.As>(ref this.input[i]); + a = a * v; + Unsafe.As>(ref this.result[i]) = a; + } + } + } + + public abstract class Divide : SIMDBenchmarkBase + { + [Benchmark] + public void Simd() + { + Vector v = this.testVector; + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = Unsafe.As>(ref this.input[i]); + a = a / v; + Unsafe.As>(ref this.result[i]) = a; + } + } + } + + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 73fc88648..147f66f8f 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -1,11 +1,11 @@ -namespace ImageSharp.Benchmarks.General.Vectorization +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization { using System; using System.Numerics; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; /// /// This benchmark compares different methods for fetching memory data into diff --git a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs index 1d4ed1193..674aef84c 100644 --- a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs +++ b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; - - using ImageSharp.Memory; + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Memory; public class CopyPixels : BenchmarkBase { @@ -103,8 +103,8 @@ namespace ImageSharp.Benchmarks.Image Configuration.Default.ParallelOptions, y => { - Span sourceRow = source.GetRowSpan(y); - Span targetRow = target.GetRowSpan(y); + Span sourceRow = source.Frames.RootFrame.GetPixelRowSpan(y); + Span targetRow = target.Frames.RootFrame.GetPixelRowSpan(y); for (int x = 0; x < source.Width; x++) { diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs index 6f3a38b7f..142675fa7 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs index ae1add172..e295aee73 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.IO; using BenchmarkDotNet.Attributes; - using ImageSharp; + using SixLabors.ImageSharp; using SixLabors.Primitives; using CoreImage = ImageSharp.Image; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs index 3f908d362..a65c9f929 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs index ece93f912..60bf49eba 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs @@ -3,31 +3,44 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes.Jobs; + + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.Tests; using CoreImage = ImageSharp.Image; using CoreSize = SixLabors.Primitives.Size; - + + [Config(typeof(Config.Short))] public class DecodeJpeg : BenchmarkBase { private byte[] jpegBytes; + private string TestImageFullPath => Path.Combine( + TestEnvironment.InputImagesDirectoryFullPath, + this.TestImage); + + [Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] + public string TestImage { get; set; } + [GlobalSetup] public void ReadImages() { if (this.jpegBytes == null) { - this.jpegBytes = File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Jpg/Baseline/Calliphora.jpg"); + this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); } } - [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] + [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] public Size JpegSystemDrawing() { using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) @@ -39,12 +52,24 @@ namespace ImageSharp.Benchmarks.Image } } - [Benchmark(Description = "ImageSharp Jpeg")] - public CoreSize JpegCore() + [Benchmark(Description = "Decode Jpeg - ImageSharp")] + public CoreSize JpegImageSharpOrig() + { + using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) + { + using (Image image = CoreImage.Load(memoryStream, new OrigJpegDecoder())) + { + return new CoreSize(image.Width, image.Height); + } + } + } + + [Benchmark(Description = "Decode Jpeg - ImageSharp PdfJs")] + public CoreSize JpegImageSharpPdfJs() { using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) { - using (Image image = CoreImage.Load(memoryStream)) + using (Image image = CoreImage.Load(memoryStream, new PdfJsJpegDecoder())) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs index 44c90d253..0577c294e 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs @@ -3,11 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Collections.Generic; + using System.IO; + using BenchmarkDotNet.Attributes; + using ImageSharp.Formats; + using ImageSharp.Formats.Jpeg.GolangPort; + using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats.Jpeg; using CoreImage = ImageSharp.Image; [Config(typeof(Config.Short))] @@ -15,19 +21,20 @@ namespace ImageSharp.Benchmarks.Image { protected override IEnumerable InputImageSubfoldersOrFiles => new[] { - "Jpg/" + "Jpg/baseline", + "Jpg/progressive", }; protected override IEnumerable SearchPatterns => new[] { "*.jpg" }; [Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] - public void DecodeJpegImageSharp() + public void DecodeJpegImageSharpNwq() { this.ForEachStream( ms => CoreImage.Load(ms) ); } - + [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] public void DecodeJpegSystemDrawing() { @@ -35,6 +42,5 @@ namespace ImageSharp.Benchmarks.Image System.Drawing.Image.FromStream ); } - } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs index be1633c04..b9a9d5cfa 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.IO; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs index 4941e17cb..86994f2d3 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs index 852e17572..e83712ffc 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs @@ -3,14 +3,15 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Collections.Generic; using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Bmp; [Config(typeof(Config.Short))] public class EncodeBmpMultiple : MultiImageBenchmarkBase.WithImagesPreloaded diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs index e6600594f..7a6ddd79d 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs index 8d1d14746..571299812 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGifMultiple.cs @@ -1,4 +1,4 @@ -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Collections.Generic; using System.Drawing.Imaging; @@ -6,7 +6,8 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Gif; [Config(typeof(SingleRunConfig))] public class EncodeGifMultiple : MultiImageBenchmarkBase.WithImagesPreloaded diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index 02e3211a7..70ea164d6 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -3,15 +3,16 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.IO; using BenchmarkDotNet.Attributes; - using ImageSharp; - using ImageSharp.Formats; - using ImageSharp.Quantizers; + using SixLabors.ImageSharp; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.Quantizers; using CoreImage = ImageSharp.Image; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs index 07c2560fd..48fd6e9e7 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs index bbf838a9e..efd69d329 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpegMultiple.cs @@ -3,16 +3,16 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Collections.Generic; using System.Drawing.Imaging; using BenchmarkDotNet.Attributes; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Jpeg; - [Config(typeof(Config.Short))] // It's long enough to iterate through multiple files public class EncodeJpegMultiple : MultiImageBenchmarkBase.WithImagesPreloaded { diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 81bb235ee..8c9fcbbb3 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using System.Drawing.Imaging; @@ -11,9 +11,10 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; - using ImageSharp.Formats; - using ImageSharp.Quantizers; - + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.Quantizers; + using SixLabors.ImageSharp.Quantizers.Base; using CoreImage = ImageSharp.Image; public class EncodePng : BenchmarkBase @@ -66,8 +67,8 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - Quantizer quantizer = this.UseOctreeQuantizer - ? (Quantizer) + QuantizerBase quantizer = this.UseOctreeQuantizer + ? (QuantizerBase) new OctreeQuantizer() : new PaletteQuantizer(); diff --git a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs index 28616213b..f9469e5fe 100644 --- a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System.Drawing; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using SystemColor = System.Drawing.Color; diff --git a/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs b/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs index f68f4a418..8b25ba6fe 100644 --- a/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs +++ b/tests/ImageSharp.Benchmarks/Image/ImageBenchmarkTests.cs @@ -12,7 +12,7 @@ #if TEST // ReSharper disable InconsistentNaming -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System; using System.Collections.Generic; diff --git a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs index e01a5951e..2ed2d42c0 100644 --- a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs @@ -4,7 +4,7 @@ // -namespace ImageSharp.Benchmarks.Image +namespace SixLabors.ImageSharp.Benchmarks.Image { using System; using System.Collections.Generic; @@ -15,6 +15,8 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; + using SixLabors.ImageSharp.Tests; + using CoreImage = ImageSharp.Image; public abstract class MultiImageBenchmarkBase : BenchmarkBase @@ -39,14 +41,14 @@ namespace ImageSharp.Benchmarks.Image [Params(InputImageCategory.AllImages, InputImageCategory.SmallImagesOnly, InputImageCategory.LargeImagesOnly)] public virtual InputImageCategory InputCategory { get; set; } - protected virtual string BaseFolder => "../ImageSharp.Tests/TestImages/Formats/"; + protected virtual string BaseFolder => TestEnvironment.InputImagesDirectoryFullPath; protected virtual IEnumerable SearchPatterns => new[] { "*.*" }; /// /// Gets the file names containing these strings are substrings are not processed by the benchmark. /// - protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof" }; + protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; /// /// Enumerates folders containing files OR files to be processed by the benchmark. diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index f488e0546..417e849be 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -3,11 +3,17 @@ netcoreapp1.1;net461 Exe True + SixLabors.ImageSharp.Benchmarks + ImageSharp.Benchmarks win7-x64 false + + + + diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index 19904a5ad..5a3131f79 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -3,17 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using CoreSize = SixLabors.Primitives.Size; using System.Numerics; - using ImageSharp.Memory; - using ImageSharp.PixelFormats.PixelBlenders; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.PixelFormats.PixelBlenders; public class PorterDuffBulkVsPixel : BenchmarkBase { diff --git a/tests/ImageSharp.Benchmarks/Program.cs b/tests/ImageSharp.Benchmarks/Program.cs index 789068426..4dd63067a 100644 --- a/tests/ImageSharp.Benchmarks/Program.cs +++ b/tests/ImageSharp.Benchmarks/Program.cs @@ -3,11 +3,11 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Running; - using ImageSharp.Formats; + using SixLabors.ImageSharp.Formats; using System.Reflection; public class Program diff --git a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs index cb13378a1..3681ff6f2 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using CoreSize = SixLabors.Primitives.Size; @@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.Crop(100, 100); + image.Mutate(x => x.Crop(100, 100)); return new CoreSize(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index 1f69030a0..f65957128 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -3,13 +3,13 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.IO; using BenchmarkDotNet.Attributes; - using ImageSharp.Processing; + using SixLabors.ImageSharp.Processing; using CoreImage = ImageSharp.Image; @@ -38,17 +38,17 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp DetectEdges")] public void ImageProcessorCoreDetectEdges() { - this.image.DetectEdges(EdgeDetection.Kayyali); - this.image.DetectEdges(EdgeDetection.Kayyali); - this.image.DetectEdges(EdgeDetection.Kirsch); - this.image.DetectEdges(EdgeDetection.Lapacian3X3); - this.image.DetectEdges(EdgeDetection.Lapacian5X5); - this.image.DetectEdges(EdgeDetection.LaplacianOfGaussian); - this.image.DetectEdges(EdgeDetection.Prewitt); - this.image.DetectEdges(EdgeDetection.RobertsCross); - this.image.DetectEdges(EdgeDetection.Robinson); - this.image.DetectEdges(EdgeDetection.Scharr); - this.image.DetectEdges(EdgeDetection.Sobel); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Kayyali)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Kayyali)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Kirsch)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Lapacian3X3)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Lapacian5X5)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.LaplacianOfGaussian)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Prewitt)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.RobertsCross)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Robinson)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Scharr)); + this.image.Mutate(x => x.DetectEdges(EdgeDetection.Sobel)); } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 2aa8df96c..884ae3501 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -3,20 +3,22 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; - using ImageSharp.Processing.Processors; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing.Processors; using CoreSize = SixLabors.Primitives.Size; - using ImageSharp.Processing; + using SixLabors.ImageSharp.Processing; using System.Numerics; using System; using System.Threading.Tasks; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; using SixLabors.Primitives; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.Advanced; public class Glow : BenchmarkBase { @@ -26,7 +28,7 @@ namespace ImageSharp.Benchmarks [GlobalSetup] public void Setup() { - this.bulk = new GlowProcessor(NamedColors.Beige, GraphicsOptions.Default) { Radius = 800 * .5f, }; + this.bulk = new GlowProcessor(NamedColors.Beige, 800 * .5f, GraphicsOptions.Default); this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; } @@ -35,7 +37,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.ApplyProcessor(bulk, image.Bounds); + this.bulk.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); } } @@ -45,7 +47,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(800, 800)) { - image.ApplyProcessor(parallel, image.Bounds); + this.parallel.Apply(image, image.Bounds()); return new CoreSize(image.Width, image.Height); } } @@ -73,7 +75,7 @@ namespace ImageSharp.Benchmarks public float Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -112,7 +114,7 @@ namespace ImageSharp.Benchmarks Parallel.For( minY, maxY, - this.ParallelOptions, + configuration.ParallelOptions, y => { int offsetY = y - startY; diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 387340069..6e7e2c8c4 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Benchmarks +namespace SixLabors.ImageSharp.Benchmarks { using System.Drawing; using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.PixelFormats; using CoreSize = SixLabors.Primitives.Size; @@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks { using (Image image = new Image(2000, 2000)) { - image.Resize(400, 400); + image.Mutate(x => x.Resize(400, 400)); return new CoreSize(image.Width, image.Height); } } diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index c62f0519a..b186ff4df 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -5,20 +5,22 @@ win7-x64 True false - ImageSharp.Sandbox46 + SixLabors.ImageSharp.Sandbox46 A cross-platform library for processing of image files written in C# Copyright © James Jackson-South and contributors. James Jackson-South and contributors James Jackson-South + SixLabors.ImageSharp.Sandbox46 - - + + + diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 6f93df16e..f3875e24a 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -3,15 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Sandbox46 +namespace SixLabors.ImageSharp.Sandbox46 { using System; using System.Runtime.DesignerServices; - using ImageSharp.Tests; - using ImageSharp.Tests.Colors; - using ImageSharp.Tests.PixelFormats; - using ImageSharp.Tests.Processing.Transforms; + using SixLabors.ImageSharp.Tests; + using SixLabors.ImageSharp.Tests.Colors; + using SixLabors.ImageSharp.Tests.Formats.Jpg; + using SixLabors.ImageSharp.Tests.PixelFormats; + using SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; + using SixLabors.ImageSharp.Tests.Processing.Transforms; using Xunit.Abstractions; @@ -39,14 +41,21 @@ namespace ImageSharp.Sandbox46 /// public static void Main(string[] args) { + RunJpegColorProfilingTests(); + // RunDecodeJpegProfilingTests(); // RunToVector4ProfilingTest(); - - RunResizeProfilingTest(); + // RunResizeProfilingTest(); Console.ReadLine(); } + private static void RunJpegColorProfilingTests() + { + new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(false); + new JpegColorConverterTests(new ConsoleOutput()).BenchmarkYCbCr(true); + } + private static void RunResizeProfilingTest() { ResizeProfilingBenchmarks test = new ResizeProfilingBenchmarks(new ConsoleOutput()); @@ -55,7 +64,7 @@ namespace ImageSharp.Sandbox46 private static void RunToVector4ProfilingTest() { - PixelOperationsTests.Color32 tests = new PixelOperationsTests.Color32(new ConsoleOutput()); + PixelOperationsTests.Rgba32 tests = new PixelOperationsTests.Rgba32(new ConsoleOutput()); tests.Benchmark_ToVector4(); } @@ -66,7 +75,7 @@ namespace ImageSharp.Sandbox46 foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) { string fileName = (string)data[0]; - benchmarks.DecodeJpeg(fileName); + benchmarks.DecodeJpeg_Original(fileName); } } } diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs new file mode 100644 index 000000000..37696987c --- /dev/null +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public abstract class BaseImageOperationsExtensionTest + { + protected readonly IImageProcessingContext operations; + private readonly FakeImageOperationsProvider.FakeImageOperations internalOperations; + protected readonly Rectangle rect; + protected readonly GraphicsOptions options; + + public BaseImageOperationsExtensionTest() + { + this.options = new GraphicsOptions(false) { }; + this.rect = new Rectangle(91, 123, 324, 56); // make this random? + this.internalOperations = new FakeImageOperationsProvider.FakeImageOperations(null, false); + this.operations = this.internalOperations; + } + + public T Verify(int index = 0) + { + Assert.InRange(index, 0, this.internalOperations.applied.Count - 1); + + var operation = this.internalOperations.applied[index]; + + return Assert.IsType(operation.Processor); + } + + public T Verify(Rectangle rect, int index = 0) + { + Assert.InRange(index, 0, this.internalOperations.applied.Count - 1); + + var operation = this.internalOperations.applied[index]; + + Assert.Equal(rect, operation.Rectangle); + return Assert.IsType(operation.Processor); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs index f2d8c92d2..426945989 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// @@ -28,7 +30,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, 50, 180, 100, -50, 0)] [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] @@ -55,7 +56,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, -50, 0, 100, 50, 180)] [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs index d263bbe18..17fd1db50 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// @@ -28,7 +30,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, 50, 180, 100, -50, 0)] [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] @@ -55,7 +56,6 @@ [InlineData(100, 0, 0, 100, 0, 0)] [InlineData(100, -50, 0, 100, 50, 180)] [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs index 9bd8b17c7..76d76f236 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs index 5589e9753..b18bd56dc 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs index 9b441f452..1652c5392 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs index 5ad224eaf..1c48d00ff 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs index 868d42975..f63c54212 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs index 02095b31f..87dc59907 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs @@ -1,13 +1,14 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; - using ImageSharp.ColorSpaces.Conversion.Implementation.Lms; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests methods. /// Test data generated using: diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs index da58ddcda..f0ac56f4f 100644 --- a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs @@ -1,40 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces { - using System; - using System.Numerics; - using Xunit; - - using ImageSharp.ColorSpaces; - /// /// Test implementations of IEquatable and IAlmostEquatable in our colorspaces /// public class ColorSpaceEqualityTests { - public static readonly TheoryData EmptyData = - new TheoryData + internal static readonly Dictionary EmptyDataLookup = + new Dictionary { - CieLab.Empty, - CieLch.Empty, - CieLchuv.Empty, - CieLuv.Empty, - CieXyz.Empty, - CieXyy.Empty, - Hsl.Empty, - Hsl.Empty, - HunterLab.Empty, - Lms.Empty, - LinearRgb.Empty, - Rgb.Empty, - YCbCr.Empty + {nameof( CieLab), CieLab.Empty }, + {nameof( CieLch), CieLch.Empty }, + {nameof( CieLchuv), CieLchuv.Empty }, + {nameof( CieLuv), CieLuv.Empty }, + {nameof( CieXyz), CieXyz.Empty }, + {nameof( CieXyy), CieXyy.Empty }, + {nameof( Hsl), Hsl.Empty }, + {nameof( HunterLab), HunterLab.Empty }, + {nameof( Lms), Lms.Empty }, + {nameof( LinearRgb), LinearRgb.Empty }, + {nameof( Rgb), Rgb.Empty }, + {nameof( YCbCr), YCbCr.Empty } }; + public static readonly IEnumerable EmptyData = EmptyDataLookup.Select(x => new [] { x.Key }); + public static readonly TheoryData EqualityData = new TheoryData { @@ -142,10 +142,11 @@ namespace ImageSharp.Tests.Colorspaces [Theory] [MemberData(nameof(EmptyData))] - public void Vector_Equals_WhenTrue(IColorVector color) + public void Vector_Equals_WhenTrue(string color) { + IColorVector colorVector = EmptyDataLookup[color]; // Act - bool equal = color.Vector.Equals(Vector3.Zero); + bool equal = colorVector.Vector.Equals(Vector3.Zero); // Assert Assert.True(equal); diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs index 7a4520a57..ee71eefc1 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs @@ -1,11 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs index 8fe64e915..6c3d579b4 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs index fa02f887f..a7071e883 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs index f8d8c773a..0dc58a0a3 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs index 3f741ea3d..0eb1f620b 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs @@ -1,12 +1,13 @@ -namespace ImageSharp.Tests.Colorspaces -{ - using System.Collections.Generic; - - using ImageSharp.ColorSpaces; - using ImageSharp.ColorSpaces.Conversion; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ /// /// Tests - conversions. /// diff --git a/tests/ImageSharp.Tests/Common/ConstantsTests.cs b/tests/ImageSharp.Tests/Common/ConstantsTests.cs index be4addf91..48ecbbbc9 100644 --- a/tests/ImageSharp.Tests/Common/ConstantsTests.cs +++ b/tests/ImageSharp.Tests/Common/ConstantsTests.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Common -{ - using Xunit; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Common +{ public class ConstantsTests { [Fact] diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs new file mode 100644 index 000000000..44762a243 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -0,0 +1,241 @@ +using System; +using System.Numerics; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Common +{ + using System.Linq; + using System.Runtime.CompilerServices; + + using Xunit.Abstractions; + using Xunit.Sdk; + + public class SimdUtilsTests + { + private ITestOutputHelper Output { get; } + + public SimdUtilsTests(ITestOutputHelper output) + { + this.Output = output; + } + + private static int R(float f) => (int)MathF.Round(f, MidpointRounding.AwayFromZero); + + private static int Re(float f) => (int)MathF.Round(f, MidpointRounding.ToEven); + + // TODO: Move this to a proper test class! + [Theory] + [InlineData(0.32, 54.5, -3.5, -4.1)] + [InlineData(5.3, 536.4, 4.5, 8.1)] + public void PseudoRound(float x, float y, float z, float w) + { + var v = new Vector4(x, y, z, w); + + Vector4 actual = v.PseudoRound(); + + Assert.Equal( + R(v.X), + (int)actual.X + ); + Assert.Equal( + R(v.Y), + (int)actual.Y + ); + Assert.Equal( + R(v.Z), + (int)actual.Z + ); + Assert.Equal( + R(v.W), + (int)actual.W + ); + } + + private static Vector CreateExactTestVector1() + { + float[] data = new float[Vector.Count]; + + data[0] = 0.1f; + data[1] = 0.4f; + data[2] = 0.5f; + data[3] = 0.9f; + + for (int i = 4; i < Vector.Count; i++) + { + data[i] = data[i - 4] + 100f; + } + return new Vector(data); + } + + private static Vector CreateRandomTestVector(int seed, float min, float max) + { + float[] data = new float[Vector.Count]; + Random rnd = new Random(); + for (int i = 0; i < Vector.Count; i++) + { + float v = (float)rnd.NextDouble() * (max-min) + min; + data[i] = v; + } + return new Vector(data); + } + + [Fact] + public void FastRound() + { + Vector v = CreateExactTestVector1(); + Vector r = v.FastRound(); + + this.Output.WriteLine(r.ToString()); + + AssertEvenRoundIsCorrect(r, v); + } + + [Theory] + [InlineData(1, 1f)] + [InlineData(1, 10f)] + [InlineData(1, 1000f)] + [InlineData(42, 1f)] + [InlineData(42, 10f)] + [InlineData(42, 1000f)] + public void FastRound_RandomValues(int seed, float scale) + { + Vector v = CreateRandomTestVector(seed, -scale*0.5f, scale*0.5f); + Vector r = v.FastRound(); + + this.Output.WriteLine(v.ToString()); + this.Output.WriteLine(r.ToString()); + + AssertEvenRoundIsCorrect(r, v); + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 8)] + [InlineData(2, 16)] + [InlineData(3, 128)] + public void BulkConvertNormalizedFloatToByte_WithRoundedData(int seed, int count) + { + float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, 0, 256); + float[] normalized = orig.Select(f => f / 255f).ToArray(); + + byte[] dest = new byte[count]; + + SimdUtils.BulkConvertNormalizedFloatToByte(normalized, dest); + + byte[] expected = orig.Select(f => (byte)(f)).ToArray(); + + Assert.Equal(expected, dest); + } + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 8)] + [InlineData(2, 16)] + [InlineData(3, 128)] + public void BulkConvertNormalizedFloatToByte_WithNonRoundedData(int seed, int count) + { + float[] source = new Random(seed).GenerateRandomFloatArray(count, 0, 1f); + + byte[] dest = new byte[count]; + + SimdUtils.BulkConvertNormalizedFloatToByte(source, dest); + + byte[] expected = source.Select(f => (byte)Math.Round(f*255f)).ToArray(); + + Assert.Equal(expected, dest); + } + + private static float Clamp255(float x) => MathF.Min(255f, MathF.Max(0f, x)); + + [Theory] + [InlineData(1, 0)] + [InlineData(1, 8)] + [InlineData(2, 16)] + [InlineData(3, 128)] + public void BulkConvertNormalizedFloatToByteClampOverflows(int seed, int count) + { + float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, -50, 444); + float[] normalized = orig.Select(f => f / 255f).ToArray(); + + byte[] dest = new byte[count]; + + SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(normalized, dest); + + byte[] expected = orig.Select(f => (byte)Clamp255(f)).ToArray(); + + Assert.Equal(expected, dest); + } + + [Theory] + [InlineData(0)] + [InlineData(7)] + [InlineData(42)] + [InlineData(255)] + [InlineData(256)] + [InlineData(257)] + private void MagicConvertToByte(float value) + { + byte actual = MagicConvert(value / 256f); + byte expected = (byte)value; + + Assert.Equal(expected, actual); + } + + [Fact] + private void BulkConvertNormalizedFloatToByte_Step() + { + float[] source = {0, 7, 42, 255, 0.5f, 1.1f, 2.6f, 16f}; + byte[] expected = source.Select(f => (byte)Math.Round(f)).ToArray(); + + source = source.Select(f => f / 255f).ToArray(); + + byte[] dest = new byte[8]; + + this.MagicConvert(source, dest); + + Assert.Equal(expected, dest); + } + + private static byte MagicConvert(float x) + { + float f = 32768.0f + x; + uint i = Unsafe.As(ref f); + return (byte)i; + } + + private void MagicConvert(Span source, Span dest) + { + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + Vector x = source.NonPortableCast>()[0]; + + x = (x * scale) + magick; + + SimdUtils.Octet.OfUInt32 ii = default(SimdUtils.Octet.OfUInt32); + + ref Vector iiRef = ref Unsafe.As>(ref ii); + + iiRef = x; + + //SimdUtils.Octet.OfUInt32 ii = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x); + + ref SimdUtils.Octet.OfByte d = ref dest.NonPortableCast()[0]; + d.LoadFrom(ref ii); + + this.Output.WriteLine(ii.ToString()); + this.Output.WriteLine(d.ToString()); + } + + private static void AssertEvenRoundIsCorrect(Vector r, Vector v) + { + for (int i = 0; i < Vector.Count; i++) + { + int actual = (int)r[i]; + int expected = Re(v[i]); + Assert.Equal(expected, actual); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 417588180..18d4abdd1 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - using Moq; - using Xunit; - /// /// Tests the configuration class. /// @@ -33,15 +30,15 @@ namespace ImageSharp.Tests [Fact] public void DefaultsToLocalFileSystem() { - Assert.IsType(DefaultConfiguration.FileSystem); - Assert.IsType(ConfigurationEmpty.FileSystem); + Assert.IsType(this.DefaultConfiguration.FileSystem); + Assert.IsType(this.ConfigurationEmpty.FileSystem); } [Fact] public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded() { - Assert.Equal(4, DefaultConfiguration.ImageEncoders.Count()); - Assert.Equal(4, DefaultConfiguration.ImageDecoders.Count()); + Assert.Equal(4, this.DefaultConfiguration.ImageEncoders.Count()); + Assert.Equal(4, this.DefaultConfiguration.ImageDecoders.Count()); } /// @@ -77,7 +74,7 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - DefaultConfiguration.AddImageFormatDetector(null); + this.DefaultConfiguration.AddImageFormatDetector(null); }); } @@ -86,15 +83,15 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - DefaultConfiguration.SetEncoder(null, new Mock().Object); + this.DefaultConfiguration.SetEncoder(null, new Mock().Object); }); Assert.Throws(() => { - DefaultConfiguration.SetEncoder(ImageFormats.Bitmap, null); + this.DefaultConfiguration.SetEncoder(ImageFormats.Bmp, null); }); Assert.Throws(() => { - DefaultConfiguration.SetEncoder(null, null); + this.DefaultConfiguration.SetEncoder(null, null); }); } @@ -103,29 +100,29 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - DefaultConfiguration.SetDecoder(null, new Mock().Object); + this.DefaultConfiguration.SetDecoder(null, new Mock().Object); }); Assert.Throws(() => { - DefaultConfiguration.SetDecoder(ImageFormats.Bitmap, null); + this.DefaultConfiguration.SetDecoder(ImageFormats.Bmp, null); }); Assert.Throws(() => { - DefaultConfiguration.SetDecoder(null, null); + this.DefaultConfiguration.SetDecoder(null, null); }); } [Fact] public void RegisterMimeTypeEncoderReplacesLast() { - var encoder1 = new Mock().Object; - ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); - var found = ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); + IImageEncoder encoder1 = new Mock().Object; + this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); + IImageEncoder found = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); Assert.Equal(encoder1, found); - var encoder2 = new Mock().Object; - ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); - var found2 = ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); + IImageEncoder encoder2 = new Mock().Object; + this.ConfigurationEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); + IImageEncoder found2 = this.ConfigurationEmpty.FindEncoder(TestFormat.GlobalTestFormat); Assert.Equal(encoder2, found2); Assert.NotEqual(found, found2); } @@ -133,14 +130,14 @@ namespace ImageSharp.Tests [Fact] public void RegisterMimeTypeDecoderReplacesLast() { - var decoder1 = new Mock().Object; - ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); - var found = ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); + IImageDecoder decoder1 = new Mock().Object; + this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); + IImageDecoder found = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); Assert.Equal(decoder1, found); - var decoder2 = new Mock().Object; - ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); - var found2 = ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); + IImageDecoder decoder2 = new Mock().Object; + this.ConfigurationEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); + IImageDecoder found2 = this.ConfigurationEmpty.FindDecoder(TestFormat.GlobalTestFormat); Assert.Equal(decoder2, found2); Assert.NotEqual(found, found2); } diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs index 7ddc2de5a..5ffd9f5f1 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs @@ -1,38 +1,34 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using Drawing; - using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class Beziers : FileTestBase { [Fact] public void ImageShouldBeOverlayedByBezierLine() { - string path = this.CreateOutputDirectory("Drawing", "BezierLine"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); using (Image image = new Image(500, 500)) { - image.BackgroundColor(Rgba32.Blue) + image.Mutate(x => x.BackgroundColor(Rgba32.Blue) .DrawBeziers(Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[] { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - }) - .Save($"{path}/Simple.png"); + })); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -57,13 +53,13 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedBezierLineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "BezierLine"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "BezierLine"); Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); using (Image image = new Image(500, 500)) { - image.BackgroundColor(Rgba32.Blue) + image.Mutate(x => x.BackgroundColor(Rgba32.Blue) .DrawBeziers(color, 10, new SixLabors.Primitives.PointF[]{ @@ -71,8 +67,8 @@ namespace ImageSharp.Tests.Drawing new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) - }) - .Save($"{path}/Opacity.png"); + })); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index 40a1cb3d3..df029d2d7 100644 --- a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System; - using System.Linq; - using System.Collections.Generic; - using System.Text; - using ImageSharp.PixelFormats; - using Xunit; - using SixLabors.Primitives; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class BlendedShapes { public static IEnumerable modes = ((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))) @@ -27,11 +25,12 @@ namespace ImageSharp.Tests.Drawing { 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.Mutate(x=>x + .Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)) + .Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + })); img.DebugSave(provider, new { mode }); } } @@ -45,15 +44,15 @@ namespace ImageSharp.Tests.Drawing { 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) + img.Mutate(x=>x.Fill(NamedColors.DarkBlue, new Rectangle(0* scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); + img.Mutate(x => x.Fill(NamedColors.HotPink, new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); - img.Fill(NamedColors.Transparent, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + })); + img.Mutate(x => x.Fill(NamedColors.Transparent, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); img.DebugSave(provider, new { mode }); } } @@ -67,20 +66,20 @@ namespace ImageSharp.Tests.Drawing { 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) + img.Mutate(x=>x.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40, 100 * scaleX, 20* scaleY))); + img.Mutate(x => x.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.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + img.Mutate(x => x.Fill(pixel, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); img.DebugSave(provider, new { mode }); } } @@ -96,16 +95,11 @@ namespace ImageSharp.Tests.Drawing { 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.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) + img.Mutate(x => x.Fill(NamedColors.DarkBlue, new Rectangle(0 * scaleX, 40* scaleY, 100 * scaleX, 20 * scaleY))); + img.Mutate(x => x.Fill(NamedColors.Black, new SixLabors.Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY), new ImageSharp.GraphicsOptions(true) { BlenderMode = mode - }); + })); img.DebugSave(provider, new { mode }); } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 728ed749a..94dd903b4 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.IO; - using System.Linq; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class DrawImageTest : FileTestBase { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; @@ -40,8 +38,8 @@ namespace ImageSharp.Tests using (Image image = provider.GetImage()) using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { - image.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) - .DebugSave(provider, new { mode }); + image.Mutate(x => x.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4))); + image.DebugSave(provider, new { mode }); } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index da5028f04..429acafb9 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -1,26 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using ShapePath = SixLabors.Shapes.Path; - using SixLabors.Shapes; - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; - using ImageSharp.Drawing.Pens; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; +using ShapePath = SixLabors.Shapes.Path; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class DrawPathTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPath() { - string path = this.CreateOutputDirectory("Drawing", "Path"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); using (Image image = new Image(500, 500)) { LinearLineSegment linerSegemnt = new LinearLineSegment( @@ -34,10 +30,10 @@ namespace ImageSharp.Tests.Drawing ShapePath p = new ShapePath(linerSegemnt, bazierSegment); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, p) - .Save($"{path}/Simple.png"); + .Draw(Rgba32.HotPink, 5, p)); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -54,7 +50,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPathWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "Path"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); @@ -74,10 +70,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(color, 10, p) - .Save($"{path}/Opacity.png"); + .Draw(color, 10, p)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); @@ -98,15 +94,15 @@ namespace ImageSharp.Tests.Drawing public void PathExtendingOffEdgeOfImageShouldNotBeCropped() { - string path = this.CreateOutputDirectory("Drawing", "Path"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Path"); using (var image = new Image(256, 256)) { - image.Fill(Rgba32.Black); + image.Mutate(x => x.Fill(Rgba32.Black)); var pen = Pens.Solid(Rgba32.White, 5f); for (int i = 0; i < 300; i += 20) { - image.DrawLines(pen, new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) }); + image.Mutate(x => x.DrawLines(pen, new SixLabors.Primitives.PointF[] { new Vector2(100, 2), new Vector2(-10, i) })); } image diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs index bbabdf0ea..d37058f5d 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs @@ -1,29 +1,26 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System; - using System.IO; - - using ImageSharp.Drawing; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class FillPatternBrushTests : FileTestBase { private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) { - string path = this.CreateOutputDirectory("Fill", "PatternBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "PatternBrush"); using (Image image = new Image(20, 20)) { - image + image.Mutate(x => x .Fill(background) - .Fill(brush); + .Fill(brush)); image.Save($"{path}/{name}.png"); @@ -51,7 +48,8 @@ namespace ImageSharp.Tests.Drawing } } } - image.Resize(80, 80).Save($"{path}/{name}x4.png"); + image.Mutate(x => x.Resize(80, 80)); + image.Save($"{path}/{name}x4.png"); } } diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index b04a41385..db6c1157c 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing -{ - using ImageSharp; - using Xunit; - using ImageSharp.Drawing; - using ImageSharp.Drawing.Processors; - using Moq; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using ImageSharp.PixelFormats; - using System; - using ImageSharp.Drawing.Pens; - using System.Numerics; +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; +using SixLabors.ImageSharp.Drawing.Brushes; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class FillRegionProcessorTests { [Theory] @@ -37,7 +39,7 @@ namespace ImageSharp.Tests.Drawing Image img = new Image(1, 1); processor.Apply(img, bounds); - region.Verify(x => x.Scan(It.IsAny(), It.IsAny>()), Times.Exactly(4)); + region.Verify(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(4)); } [Fact] @@ -51,7 +53,7 @@ namespace ImageSharp.Tests.Drawing region.Setup(x => x.Bounds).Returns(bounds); region.Setup(x => x.MaxIntersections).Returns(10); - region.Setup(x => x.Scan(It.IsAny(), It.IsAny>())) + region.Setup(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny())) .Returns>((y, span) => { if (y < 5) @@ -71,18 +73,16 @@ namespace ImageSharp.Tests.Drawing processor.Apply(img, bounds); } - - [Fact] public void DrawOffCanvas() { using (var img = new Image(10, 10)) { - img.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] { + img.Mutate(x => x.DrawLines(new Pen(Rgba32.Black, 10), new SixLabors.Primitives.PointF[] { new Vector2(-10, 5), new Vector2(20, 5), - }); + })); } } } diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index ce127cfe0..6eb139bac 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -1,31 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using Drawing; - using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class FillSolidBrushTests : FileTestBase { [Fact] public void ImageShouldBeFloodFilledWithColorOnDefaultBackground() { - string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush"); using (Image image = new Image(500, 500)) { + image.Mutate(x => x + .Fill(Rgba32.HotPink)); image - .Fill(Rgba32.HotPink) .Save($"{path}/DefaultBack.png"); using (PixelAccessor sourcePixels = image.Lock()) @@ -40,13 +37,13 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithColor() { - string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink) - .Save($"{path}/Simple.png"); + .Fill(Rgba32.HotPink)); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -60,15 +57,15 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithColorOpacity() { - string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + string path = TestEnvironment.CreateOutputDirectory("Fill", "SolidBrush"); using (Image image = new Image(500, 500)) { Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(color) - .Save($"{path}/Opacity.png"); + .Fill(color)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index e058572fb..6c0670a85 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -1,26 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System.IO; - using Xunit; - using Drawing; - using ImageSharp.Drawing; - using System.Numerics; - using ImageSharp.Drawing.Pens; - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class LineComplexPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), @@ -34,10 +31,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) - .Save($"{path}/Simple.png"); + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -68,7 +65,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -81,10 +78,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) - .Save($"{path}/SimpleVanishHole.png"); + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Save($"{path}/SimpleVanishHole.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -116,7 +113,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineOverlapping() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -129,10 +126,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) - .Save($"{path}/SimpleOverlapping.png"); + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1))); + image.Save($"{path}/SimpleOverlapping.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -159,7 +156,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineDashed() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -172,10 +169,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1)) - .Save($"{path}/Dashed.png"); + .Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1))); + image.Save($"{path}/Dashed.png"); } } @@ -183,7 +180,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "LineComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -197,10 +194,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Draw(color, 5, simplePath.Clip(hole1)) - .Save($"{path}/Opacity.png"); + .Draw(color, 5, simplePath.Clip(hole1))); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs index f47d56696..d8c5c41d8 100644 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineTests.cs @@ -1,37 +1,32 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using ImageSharp.Drawing; - using ImageSharp.Drawing.Pens; - - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class LineTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPath() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[]{ new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Simple.png"); + })); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -47,10 +42,10 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPath_NoAntialias() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Rgba32.HotPink, 5, new SixLabors.Primitives.PointF[] { @@ -58,8 +53,8 @@ namespace ImageSharp.Tests.Drawing new Vector2(200, 150), new Vector2(50, 300) }, - new GraphicsOptions(false)) - .Save($"{path}/Simple_noantialias.png"); + new GraphicsOptions(false))); + image.Save($"{path}/Simple_noantialias.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -75,132 +70,132 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPathDashed() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.Dash(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Dashed.png"); - } + })); + image.Save($"{path}/Dashed.png"); } + } [Fact] public void ImageShouldBeOverlayedByPathDotted() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.Dot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Dot.png"); - } + })); + image.Save($"{path}/Dot.png"); } + } [Fact] public void ImageShouldBeOverlayedByPathDashDot() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.DashDot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/DashDot.png"); - } + })); + image.Save($"{path}/DashDot.png"); } + } [Fact] public void ImageShouldBeOverlayedByPathDashDotDot() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); Image image = new Image(500, 500); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5), new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/DashDotDot.png"); - } + })); + image.Save($"{path}/DashDotDot.png"); + } [Fact] public void ImageShouldBeOverlayedPathWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); Image image = new Image(500, 500); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(color, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Opacity.png"); + })); + image.Save($"{path}/Opacity.png"); - //shift background color towards forground color by the opacity amount - Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[11, 11]); - Assert.Equal(mergedColor, sourcePixels[199, 149]); + Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } + } [Fact] public void ImageShouldBeOverlayedByPathOutline() { - string path = this.CreateOutputDirectory("Drawing", "Lines"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Lines"); Image image = new Image(500, 500); - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .DrawLines(Rgba32.HotPink, 10, new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 10), new Vector2(200, 150), new Vector2(10, 150) - }) - .Save($"{path}/Rectangle.png"); + })); + image.Save($"{path}/Rectangle.png"); - using (PixelAccessor sourcePixels = image.Lock()) - { - Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]); - Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - } + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } - } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index 3a2b66c3c..a88a5b09c 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - - public class FillPath : IDisposable + public class FillPath : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; @@ -22,25 +24,12 @@ namespace ImageSharp.Tests.Drawing.Paths new Vector2(20,10), new Vector2(30,10), })); - private ProcessorWatchingImage img; - - public FillPath() - { - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } [Fact] public void CorrectlySetsBrushAndPath() { - img.Fill(brush, path); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, path); + var processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -56,10 +45,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - img.Fill(brush, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, path, noneDefault); + var processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); @@ -73,10 +60,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndPath() { - img.Fill(color, path); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(color, path); + var processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -91,10 +76,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - img.Fill(color, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(color, path, noneDefault); + var processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs index 24efa976e..e8fad9d12 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - - public class FillPathCollection : IDisposable + public class FillPathCollection : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; @@ -30,29 +32,20 @@ namespace ImageSharp.Tests.Drawing.Paths })); IPathCollection pathCollection; - - private ProcessorWatchingImage img; - + public FillPathCollection() { this.pathCollection = new PathCollection(path1, path2); - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); } [Fact] public void CorrectlySetsBrushAndPath() { - img.Fill(brush, pathCollection); + this.operations.Fill(brush, pathCollection); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -69,12 +62,11 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathOptions() { - img.Fill(brush, pathCollection, noneDefault); + this.operations.Fill(brush, pathCollection, noneDefault); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(noneDefault, processor.Options); @@ -89,12 +81,11 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndPath() { - img.Fill(color, pathCollection); + this.operations.Fill(color, pathCollection); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -110,12 +101,11 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - img.Fill(color, pathCollection, noneDefault); + this.operations.Fill(color, pathCollection, noneDefault); - Assert.Equal(2, img.ProcessorApplications.Count); for (var i = 0; i < 2; i++) { - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + FillRegionProcessor processor = this.Verify>(i); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index b46d3d0d9..b390c3106 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -1,17 +1,19 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - - public class FillPolygon : IDisposable + public class FillPolygon : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; @@ -22,25 +24,14 @@ namespace ImageSharp.Tests.Drawing.Paths new Vector2(20,10), new Vector2(30,10), }; - private ProcessorWatchingImage img; - public FillPolygon() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } [Fact] public void CorrectlySetsBrushAndPath() { - img.FillPolygon(brush, path); + this.operations.FillPolygon(brush, path); - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -54,10 +45,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushPathAndOptions() { - img.FillPolygon(brush, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.FillPolygon(brush, path, noneDefault); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); @@ -71,10 +60,9 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndPath() { - img.FillPolygon(color, path); + this.operations.FillPolygon(color, path); + FillRegionProcessor processor = this.Verify>(); - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -89,10 +77,9 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorPathAndOptions() { - img.FillPolygon(color, path, noneDefault); + this.operations.FillPolygon(color, path, noneDefault); + FillRegionProcessor processor = this.Verify>(); - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index 7e6a3deee..05e6bb29b 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -1,41 +1,27 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - - using ImageSharp.Drawing.Brushes; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; - using ImageSharp.Drawing; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; +using System; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; - public class FillRectangle : IDisposable +namespace SixLabors.ImageSharp.Tests.Drawing.Paths +{ + public class FillRectangle : BaseImageOperationsExtensionTest { GraphicsOptions noneDefault = new GraphicsOptions(); Rgba32 color = Rgba32.HotPink; SolidBrush brush = Brushes.Solid(Rgba32.HotPink); SixLabors.Primitives.Rectangle rectangle = new SixLabors.Primitives.Rectangle(10, 10, 77, 76); - private ProcessorWatchingImage img; - - public FillRectangle() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - [Fact] public void CorrectlySetsBrushAndRectangle() { - img.Fill(brush, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, rectangle); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -52,10 +38,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsBrushRectangleAndOptions() { - img.Fill(brush, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(brush, rectangle, noneDefault); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); @@ -72,11 +56,9 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorAndRectangle() { - img.Fill(color, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - + this.operations.Fill(color, rectangle); + FillRegionProcessor processor = this.Verify>(); + Assert.Equal(GraphicsOptions.Default, processor.Options); ShapeRegion region = Assert.IsType(processor.Region); @@ -93,10 +75,8 @@ namespace ImageSharp.Tests.Drawing.Paths [Fact] public void CorrectlySetsColorRectangleAndOptions() { - img.Fill(color, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + this.operations.Fill(color, rectangle, noneDefault); + FillRegionProcessor processor = this.Verify>(); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs deleted file mode 100644 index 1670b3352..000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ /dev/null @@ -1,40 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Processing; - using System.Collections.Generic; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - - /// - /// Watches but does not actually run the processors against the image. - /// - /// - public class ProcessorWatchingImage : Image - { - public List ProcessorApplications { get; } = new List(); - - public ProcessorWatchingImage(int width, int height) - : base(Configuration.CreateDefaultInstance(), width, height) - { - } - - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) - { - this.ProcessorApplications.Add(new ProcessorDetails - { - processor = processor, - rectangle = rectangle - }); - } - - public struct ProcessorDetails - { - public IImageProcessor processor; - public Rectangle rectangle; - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs index e791a2bfb..5d2e93e87 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs @@ -1,21 +1,23 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using Moq; - using System.Collections.Immutable; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Processing; +using Moq; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing.Paths +{ public class ShapePathTests { // TODO readd these back in diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index df183a8af..941807f54 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -1,22 +1,24 @@ - -namespace ImageSharp.Tests.Drawing.Paths +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.Processing; +using Moq; +using SixLabors.Primitives; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Paths { - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using Moq; - using System.Collections.Immutable; - using SixLabors.Primitives; - public class ShapeRegionTests { private readonly Mock pathMock; @@ -74,17 +76,17 @@ namespace ImageSharp.Tests.Drawing.Paths int yToScan = 10; ShapeRegion region = new ShapeRegion(pathMock.Object); - pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((s, e, b) => { + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); Assert.True(s.X < bounds.Left); Assert.True(e.X > bounds.Right); }).Returns(0); - int i = region.Scan(yToScan, new float[0]); + int i = region.Scan(yToScan, new float[0], 0); - pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -93,17 +95,17 @@ namespace ImageSharp.Tests.Drawing.Paths int yToScan = 10; ShapeRegion region = new ShapeRegion(pathMock.Object); - pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>())) - .Callback>((s, e, b) => { + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); Assert.True(s.X < bounds.Left); Assert.True(e.X > bounds.Right); }).Returns(0); - int i = region.Scan(yToScan, new float[0]); + int i = region.Scan(yToScan, new float[0], 0); - pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny>()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index 501b10e4a..a43f14eb7 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -1,39 +1,36 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing { - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using Xunit; - using Drawing; - using ImageSharp.Drawing; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - public class PolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = this.CreateOutputDirectory("Drawing", "Polygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .DrawPolygon(Rgba32.HotPink, 5, - new SixLabors.Primitives.PointF[] { + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .DrawPolygon(Rgba32.HotPink, 5, + new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save($"{path}/Simple.png"); + })); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -51,7 +48,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "Polygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -62,10 +59,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .DrawPolygon(color, 10, simplePath) - .Save($"{path}/Opacity.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .DrawPolygon(color, 10, simplePath)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); @@ -86,14 +83,14 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByRectangleOutline() { - string path = this.CreateOutputDirectory("Drawing", "Polygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "Polygons"); using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140)) - .Save($"{path}/Rectangle.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140))); + image.Save($"{path}/Rectangle.png"); using (PixelAccessor sourcePixels = image.Lock()) { diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs index 9c62e860a..52668cc56 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs @@ -1,25 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.Drawing.Brushes; - using System.IO; - using System.Linq; - - using ImageSharp.PixelFormats; - - using Xunit; - using SixLabors.Primitives; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class RecolorImageTest : FileTestBase { [Fact] public void ImageShouldRecolorYellowToHotPink() { - string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage"); RecolorBrush brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); @@ -27,8 +23,8 @@ namespace ImageSharp.Tests { using (Image image = file.CreateImage()) { - image.Fill(brush) - .Save($"{path}/{file.FileName}"); + image.Mutate(x => x.Fill(brush)); + image.Save($"{path}/{file.FileName}"); } } } @@ -36,7 +32,7 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldRecolorYellowToHotPinkInARectangle() { - string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "RecolorImage"); RecolorBrush brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); @@ -45,8 +41,8 @@ namespace ImageSharp.Tests using (Image image = file.CreateImage()) { int imageHeight = image.Height; - image.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2)) - .Save($"{path}/Shaped_{file.FileName}"); + image.Mutate(x => x.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2))); + image.Save($"{path}/Shaped_{file.FileName}"); } } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index ba904cb3f..07e75acf4 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -1,25 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; - - using Xunit; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class SolidBezierTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByFilledPolygon() { - string path = this.CreateOutputDirectory("Drawing", "FilledBezier"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 400), new Vector2(30, 10), @@ -28,10 +23,10 @@ namespace ImageSharp.Tests.Drawing }; using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new Polygon(new CubicBezierLineSegment(simplePath))) - .Save($"{path}/Simple.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -49,7 +44,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonOpacity() { - string path = this.CreateOutputDirectory("Drawing", "FilledBezier"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledBezier"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 400), new Vector2(30, 10), @@ -60,10 +55,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Rgba32.Blue) - .Fill(color, new Polygon(new CubicBezierLineSegment(simplePath))) - .Save($"{path}/Opacity.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .Fill(color, new Polygon(new CubicBezierLineSegment(simplePath)))); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index c3af3d5c2..e1849b0d0 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -1,26 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing -{ - using System.IO; - using Xunit; - using Drawing; - using ImageSharp.Drawing; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing +{ public class SolidComplexPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -34,10 +30,10 @@ namespace ImageSharp.Tests.Drawing // var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20)); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, clipped) - .Save($"{path}/Simple.png"); + .Fill(Rgba32.HotPink, clipped)); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -53,7 +49,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOverlap() { - string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -66,10 +62,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, simplePath.Clip(hole1)) - .Save($"{path}/SimpleOverlapping.png"); + .Fill(Rgba32.HotPink, simplePath.Clip(hole1))); + image.Save($"{path}/SimpleOverlapping.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -84,7 +80,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "ComplexPolygon"); Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), @@ -98,10 +94,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(color, simplePath.Clip(hole1)) - .Save($"{path}/Opacity.png"); + .Fill(color, simplePath.Clip(hole1))); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 793bcfc9f..c210b66ed 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -1,28 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing { - using Drawing; - using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; - using System.IO; - using System.Numerics; - using Xunit; - using ImageSharp.Drawing.Brushes; - using ImageSharp.PixelFormats; - - using SixLabors.Shapes; - public class SolidPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByFilledPolygon() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -31,9 +28,9 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(true)) - .Save($"{path}/Simple.png"); + image.Mutate(x => x + .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(true))); + image.Save($"{path}/Simple.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -45,7 +42,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonWithPattern() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -54,9 +51,9 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image - .FillPolygon(Brushes.Horizontal(Rgba32.HotPink), simplePath, new GraphicsOptions(true)) - .Save($"{path}/Pattern.png"); + image.Mutate(x => x + .FillPolygon(Brushes.Horizontal(Rgba32.HotPink), simplePath, new GraphicsOptions(true))); + image.Save($"{path}/Pattern.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -68,7 +65,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonNoAntialias() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -77,10 +74,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(false)) - .Save($"{path}/Simple_NoAntialias.png"); + .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(false))); + image.Save($"{path}/Simple_NoAntialias.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -98,7 +95,7 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygonImage() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -110,17 +107,17 @@ namespace ImageSharp.Tests.Drawing { ImageBrush brush = new ImageBrush(brushImage); - image - .BackgroundColor(Rgba32.Blue) - .FillPolygon(brush, simplePath) - .Save($"{path}/Image.png"); + image.Mutate(x => x + .BackgroundColor(Rgba32.Blue) + .FillPolygon(brush, simplePath)); + image.Save($"{path}/Image.png"); } } [Fact] public void ImageShouldBeOverlayedByFilledPolygonOpacity() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); SixLabors.Primitives.PointF[] simplePath = new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(200, 150), @@ -130,10 +127,10 @@ namespace ImageSharp.Tests.Drawing using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .FillPolygon(color, simplePath) - .Save($"{path}/Opacity.png"); + .FillPolygon(color, simplePath)); + image.Save($"{path}/Opacity.png"); //shift background color towards forground color by the opacity amount Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); @@ -148,14 +145,14 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledRectangle() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); using (Image image = new Image(500, 500)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new SixLabors.Shapes.RectangularePolygon(10, 10, 190, 140)) - .Save($"{path}/Rectangle.png"); + .Fill(Rgba32.HotPink, new SixLabors.Shapes.RectangularePolygon(10, 10, 190, 140))); + image.Save($"{path}/Rectangle.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -175,14 +172,14 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledTriangle() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); using (Image image = new Image(100, 100)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30)) - .Save($"{path}/Triangle.png"); + .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30))); + image.Save($"{path}/Triangle.png"); using (PixelAccessor sourcePixels = image.Lock()) { @@ -196,46 +193,46 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledSeptagon() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; using (Image image = new Image(config, 100, 100)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) - .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI)) - .Save($"{path}/Septagon.png"); + .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI))); + image.Save($"{path}/Septagon.png"); } } [Fact] public void ImageShouldBeOverlayedByFilledEllipse() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; using (Image image = new Image(config, 100, 100)) { - image + image.Mutate(x => x .BackgroundColor(Rgba32.Blue) .Fill(Rgba32.HotPink, new EllipsePolygon(50, 50, 30, 50) - .Rotate((float)(Math.PI / 3))) - .Save($"{path}/ellipse.png"); + .Rotate((float)(Math.PI / 3)))); + image.Save($"{path}/ellipse.png"); } } [Fact] public void ImageShouldBeOverlayedBySquareWithCornerClipped() { - string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + string path = TestEnvironment.CreateOutputDirectory("Drawing", "FilledPolygons"); Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; using (Image image = new Image(config, 200, 200)) { - image + image.Mutate(x => x .Fill(Rgba32.Blue) .FillPolygon(Rgba32.HotPink, new SixLabors.Primitives.PointF[] { @@ -245,8 +242,8 @@ namespace ImageSharp.Tests.Drawing new Vector2( 120, 64 ), new Vector2( 120, 120 ), new Vector2( 8, 120 ) - }) - .Save($"{path}/clipped-corner.png"); + })); + image.Save($"{path}/clipped-corner.png"); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs index 05a1f1e36..3f138248e 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs @@ -1,26 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing.Text +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing.Paths; +using SixLabors.Fonts; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Text { - using System; - using System.Numerics; - - using ImageSharp.Drawing; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Drawing.Pens; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - using ImageSharp.Tests.Drawing.Paths; - - using SixLabors.Fonts; - using SixLabors.Shapes; - - using Xunit; - - public class DrawText_Path : IDisposable + public class DrawText_Path : BaseImageOperationsExtensionTest { Rgba32 color = Rgba32.HotPink; @@ -30,8 +25,6 @@ namespace ImageSharp.Tests.Drawing.Text new LinearLineSegment( new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(20, 10), new Vector2(20, 10), new Vector2(30, 10), })); - private ProcessorWatchingImage img; - private readonly FontCollection FontCollection; private readonly Font Font; @@ -40,18 +33,12 @@ namespace ImageSharp.Tests.Drawing.Text { this.FontCollection = new FontCollection(); this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")).CreateFont(12); - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - this.img.Dispose(); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPen() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -59,50 +46,49 @@ namespace ImageSharp.Tests.Drawing.Text path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, path); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSet() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenColorSet() { - this.img.DrawText("123", this.Font, Rgba32.Red, path, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Rgba32.Red, path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -111,13 +97,11 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void FillsForEachACharachterWhenColorSetDefaultOptions() { - this.img.DrawText("123", this.Font, Rgba32.Red, path); + this.operations.DrawText("123", this.Font, Rgba32.Red, path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -126,7 +110,7 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrush() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, null, @@ -134,45 +118,45 @@ namespace ImageSharp.Tests.Drawing.Text path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() { - this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), path); + this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSet() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetDefaultOptions() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -180,72 +164,49 @@ namespace ImageSharp.Tests.Drawing.Text path, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void BrushAppliesBeforPen() { - this.img.DrawText( + this.operations.DrawText( "1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path, - new TextGraphicsOptions(true)); + new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); + var processor = this.Verify>(0); + this.Verify>(1); } [Fact] public void BrushAppliesBeforPenDefaultOptions() { - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); - } + this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); - [Fact] - public void GlyphHeightChangesBasedOnuseImageResolutionFlag() - { - this.img.MetaData.VerticalResolution = 1; - this.img.MetaData.HorizontalResolution = 1; - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true) { - UseImageResolution = false - }); - - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true) - { - UseImageResolution = true - }); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); - - ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); - ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); - - // magic numbers based on the font used at well known resolutions - Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2); - Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2); + var processor = this.Verify>(0); + this.Verify>(1); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 1c8c9f1d1..d9fe1a76d 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -1,26 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Drawing.Text +using System; +using System.Numerics; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Drawing.Paths; +using SixLabors.Fonts; +using SixLabors.Shapes; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Drawing.Text { - using System; - using System.Numerics; - - using ImageSharp.Drawing; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Drawing.Pens; - using ImageSharp.Drawing.Processors; - using ImageSharp.PixelFormats; - using ImageSharp.Tests.Drawing.Paths; - - using SixLabors.Fonts; - using SixLabors.Shapes; - - using Xunit; - - public class DrawText : IDisposable + public class DrawText : BaseImageOperationsExtensionTest { Rgba32 color = Rgba32.HotPink; @@ -30,8 +25,6 @@ namespace ImageSharp.Tests.Drawing.Text new LinearLineSegment( new SixLabors.Primitives.PointF[] { new Vector2(10, 10), new Vector2(20, 10), new Vector2(20, 10), new Vector2(30, 10), })); - private ProcessorWatchingImage img; - private readonly FontCollection FontCollection; private readonly Font Font; @@ -40,18 +33,12 @@ namespace ImageSharp.Tests.Drawing.Text { this.FontCollection = new FontCollection(); this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")).CreateFont(12); - this.img = new ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - this.img.Dispose(); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPen() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -59,50 +46,49 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSet() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void FillsForEachACharachterWhenColorSet() { - this.img.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -111,13 +97,11 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void FillsForEachACharachterWhenColorSetDefaultOptions() { - this.img.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero); + this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(Rgba32.Red, brush.Color); @@ -126,7 +110,7 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrush() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, null, @@ -134,45 +118,45 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() { - this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSet() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetDefaultOptions() { - this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet() { - this.img.DrawText( + this.operations.DrawText( "123", this.Font, Brushes.Solid(Rgba32.Red), @@ -180,23 +164,32 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); + this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(6, this.img.ProcessorApplications.Count); + var processor = this.Verify>(0); + this.Verify>(1); + this.Verify>(2); + this.Verify>(3); + this.Verify>(4); + this.Verify>(5); } [Fact] public void BrushAppliesBeforPen() { - this.img.DrawText( + this.operations.DrawText( "1", this.Font, Brushes.Solid(Rgba32.Red), @@ -204,48 +197,17 @@ namespace ImageSharp.Tests.Drawing.Text Vector2.Zero, new TextGraphicsOptions(true)); - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); + var processor = this.Verify>(0); + this.Verify>(1); } [Fact] public void BrushAppliesBeforPenDefaultOptions() { - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); - } + this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); - [Fact] - public void GlyphHeightChangesBasedOnuseImageResolutionFlag() - { - this.img.MetaData.VerticalResolution = 1; - this.img.MetaData.HorizontalResolution = 1; - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true) { - UseImageResolution = false - }); - - this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true) - { - UseImageResolution = true - }); - - Assert.NotEmpty(this.img.ProcessorApplications); - Assert.Equal(2, this.img.ProcessorApplications.Count); - FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); - - ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); - ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); - - // magic numbers based on the font used at well known resolutions - Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2); - Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2); + var processor = this.Verify>(0); + this.Verify>(1); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs index ce2a5becf..079510c33 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs @@ -1,22 +1,23 @@ - -namespace ImageSharp.Tests.Drawing.Text -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using ImageSharp.Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using SixLabors.Fonts; +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Brushes; +using SixLabors.ImageSharp.Drawing.Pens; +using SixLabors.ImageSharp.Drawing.Processors; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Fonts; +using SixLabors.Shapes; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing.Text +{ public class OutputText : FileTestBase { private readonly FontCollection FontCollection; @@ -34,9 +35,9 @@ namespace ImageSharp.Tests.Drawing.Text //draws 2 overlapping triangle glyphs twice 1 set on each line using (Image img = new Image(100, 200)) { - img.Fill(Rgba32.DarkBlue) - .DrawText("AB\nAB", new Font(this.Font, 50), Rgba32.Red, new Vector2(0, 0)); - img.Save($"{this.CreateOutputDirectory("Drawing", "Text")}/AB.png"); + img.Mutate(x => x.Fill(Rgba32.DarkBlue) + .DrawText("AB\nAB", new Font(this.Font, 50), Rgba32.Red, new Vector2(0, 0))); + img.Save($"{TestEnvironment.CreateOutputDirectory("Drawing", "Text")}/AB.png"); } } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs index 1a26bcfed..975622e43 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs @@ -1,15 +1,17 @@ - -namespace ImageSharp.Tests.Drawing.Text -{ - using ImageSharp.Drawing; - using SixLabors.Fonts; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Threading.Tasks; - using Xunit; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Drawing; +using SixLabors.Fonts; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Drawing.Text +{ public class TextGraphicsOptionsTests { [Fact] diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs new file mode 100644 index 000000000..f750bfcfa --- /dev/null +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests +{ + internal class FakeImageOperationsProvider : IImageProcessingContextFactory + { + private List ImageOperators = new List(); + + public bool HasCreated(Image source) + where TPixel : struct, IPixel + { + return Created(source).Any(); + } + public IEnumerable> Created(Image source) where TPixel : struct, IPixel + { + return this.ImageOperators.OfType>() + .Where(x => x.source == source); + } + + public IEnumerable.AppliedOpperation> AppliedOperations(Image source) where TPixel : struct, IPixel + { + return Created(source) + .SelectMany(x => x.applied); + } + + public IInternalImageProcessingContext CreateImageProcessingContext(Image source, bool mutate) where TPixel : struct, IPixel + { + var op = new FakeImageOperations(source, mutate); + this.ImageOperators.Add(op); + return op; + } + + + public class FakeImageOperations : IInternalImageProcessingContext + where TPixel : struct, IPixel + { + public Image source; + + public List applied = new List(); + public bool mutate; + + public FakeImageOperations(Image source, bool mutate) + { + this.mutate = mutate; + if (mutate) + { + this.source = source; + } + else + { + this.source = source?.Clone(); + } + } + + public Image Apply() + { + return source; + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + applied.Add(new AppliedOpperation + { + Processor = processor, + Rectangle = rectangle + }); + return this; + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + applied.Add(new AppliedOpperation + { + Processor = processor + }); + return this; + } + public struct AppliedOpperation + { + public Rectangle? Rectangle { get; set; } + public IImageProcessor Processor { get; set; } + } + } + } +} diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index f9909de4a..bbb5c7bfa 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -1,18 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Collections.Generic; +using System.Collections.Generic; +namespace SixLabors.ImageSharp.Tests +{ /// /// The test base class for reading and writing to files. /// - public abstract class FileTestBase : TestBase + public abstract class FileTestBase { /// + /// TODO: We really should not depend on this! Let's use well defined, test-case specific inputs everywhere! /// A collection made up of one file for each image format /// public static IEnumerable DefaultFiles = @@ -49,6 +48,11 @@ namespace ImageSharp.Tests /// public const PixelTypes DefaultPixelType = PixelTypes.Rgba32; + /// + /// A few other pixel types to prove that a processor is not bound to a single one. + /// + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + public static class Extensions { public const string Bmp = "bmp"; @@ -70,7 +74,9 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Jpeg.Baseline.Ycck), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.Cmyk), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.Floorplan), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Jpeg.Progressive.Festzug), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.Bad.MissingEOF), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Jpeg.Baseline.Bad.ExifUndefType), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Progressive.Fb), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Progressive.Progress), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.GammaDalaiLamaGray), // Perf: Enable for local testing only @@ -79,6 +85,7 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only // TestFile.Create(TestImages.Bmp.CoreHeader), // Perf: Enable for local testing only TestFile.Create(TestImages.Png.Splash), + // TestFile.Create(TestImages.Png.SnakeGame), // TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Bad.ChunkLength1), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Bad.ChunkLength2), // Perf: Enable for local testing only diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 4e10de23f..8a50b760e 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,26 +1,38 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using ImageSharp.PixelFormats; - - using Xunit; + using SixLabors.ImageSharp.Formats.Bmp; public class BmpDecoderTests : FileTestBase { [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgb24)] - public void OpenAllBmpFiles_SaveBmp(TestImageProvider provider) + public void DecodeBmp(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider, "bmp"); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(TestImages.Bmp.F, CommonNonDefaultPixelTypes)] + public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage(new BmpDecoder())) { - provider.Utility.SaveTestOutputFile(image, "bmp"); + image.DebugSave(provider, "bmp"); + image.CompareToOriginal(provider); } } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index e5af28d8b..d96d3def5 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -using ImageSharp.Formats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using ImageSharp.PixelFormats; - - using Xunit; - public class BmpEncoderTests : FileTestBase { public static readonly TheoryData BitsPerPixel @@ -24,7 +21,7 @@ namespace ImageSharp.Tests [MemberData(nameof(BitsPerPixel))] public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel) { - string path = this.CreateOutputDirectory("Bmp"); + string path = TestEnvironment.CreateOutputDirectory("Bmp"); foreach (TestFile file in Files) { diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 4ef8fe061..473bc2b52 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.IO; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class GeneralFormatTests : FileTestBase { [Theory] @@ -22,14 +19,14 @@ namespace ImageSharp.Tests { image.MetaData.VerticalResolution = 150; image.MetaData.HorizontalResolution = 150; - image.DebugSave(provider, null, Extensions.Bmp); + image.DebugSave(provider); } } [Fact] public void ImageCanEncodeToString() { - string path = this.CreateOutputDirectory("ToString"); + string path = TestEnvironment.CreateOutputDirectory("ToString"); foreach (TestFile file in Files) { @@ -44,13 +41,13 @@ namespace ImageSharp.Tests [Fact] public void DecodeThenEncodeImageFromStreamShouldSucceed() { - string path = this.CreateOutputDirectory("Encode"); + string path = TestEnvironment.CreateOutputDirectory("Encode"); foreach (TestFile file in Files) { using (Image image = file.CreateImage()) { - image.Save($"{path}/{file.FileName}"); + image.Save($"{path}/{file.FileName}"); } } } @@ -58,37 +55,37 @@ namespace ImageSharp.Tests [Fact] public void QuantizeImageShouldPreserveMaximumColorPrecision() { - string path = this.CreateOutputDirectory("Quantize"); + string path = TestEnvironment.CreateOutputDirectory("Quantize"); foreach (TestFile file in Files) { using (Image srcImage = Image.Load(file.Bytes, out var mimeType)) { - using (Image image = new Image(srcImage)) + using (Image image = srcImage.Clone()) { using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) { - image.Quantize(Quantization.Octree) - .Save(output, mimeType); + image.Mutate(x => x.Quantize(Quantization.Octree)); + image.Save(output, mimeType); } } - using (Image image = new Image(srcImage)) + using (Image image = srcImage.Clone()) { using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) { - image.Quantize(Quantization.Wu) - .Save(output, mimeType); + image.Mutate(x => x.Quantize(Quantization.Wu)); + image.Save(output, mimeType); } } - using (Image image = new Image(srcImage)) + using (Image image = srcImage.Clone()) { using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) { - image.Quantize(Quantization.Palette) - .Save(output, mimeType); + image.Mutate(x => x.Quantize(Quantization.Palette)); + image.Save(output, mimeType); } } } @@ -98,7 +95,7 @@ namespace ImageSharp.Tests [Fact] public void ImageCanConvertFormat() { - string path = this.CreateOutputDirectory("Format"); + string path = TestEnvironment.CreateOutputDirectory("Format"); foreach (TestFile file in Files) { @@ -130,7 +127,7 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldPreservePixelByteOrderWhenSerialized() { - string path = this.CreateOutputDirectory("Serialized"); + string path = TestEnvironment.CreateOutputDirectory("Serialized"); foreach (TestFile file in Files) { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 06bfd8990..d04c49a98 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -1,17 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Text; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Text; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - public class GifDecoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; @@ -29,7 +28,20 @@ namespace ImageSharp.Tests imageProvider.Utility.SaveTestOutputFile(image, "gif"); } } - + [Theory] + [WithFileCollection(nameof(TestFiles), PixelTypes)] + public void DecodeResizeAndSave(TestImageProvider imageProvider) + where TPixel : struct, IPixel + { + using (Image image = imageProvider.GetImage()) + { + image.Mutate(x => x.Resize(new Size(image.Width / 2, image.Height / 2))); + + imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + imageProvider.Utility.SaveTestOutputFile(image, "gif"); + } + } + [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index c36539686..a06e36e2a 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -1,16 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.IO; - using Xunit; - - using ImageSharp.Formats; - using ImageSharp.PixelFormats; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class GifEncoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs deleted file mode 100644 index dc6985dd3..000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ImageSharp.Formats; -using Xunit; -using Xunit.Abstractions; -// ReSharper disable InconsistentNaming - -namespace ImageSharp.Tests -{ - using System.Numerics; - - using ImageSharp.Formats.Jpg; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - public class BadEOFJpegTests : MeasureFixture - { - public BadEOFJpegTests(ITestOutputHelper output) - : base(output) - { - } - - [Theory] - [WithFile(TestImages.Jpeg.Baseline.Bad.MissingEOF, PixelTypes.Rgba32)] - public void LoadBaselineImage(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Assert.NotNull(image); - provider.Utility.SaveTestOutputFile(image, "bmp"); - } - } - - [Theory] // TODO: #18 - [WithFile(TestImages.Jpeg.Progressive.Bad.BadEOF, PixelTypes.Rgba32)] - public void LoadProgressiveImage(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Assert.NotNull(image); - provider.Utility.SaveTestOutputFile(image, "bmp"); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs new file mode 100644 index 000000000..191cfec73 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -0,0 +1,105 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + + +// Uncomment this to turn unit tests into benchmarks: +//#define BENCHMARKING + +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + using SixLabors.Primitives; + + using Xunit; + using Xunit.Abstractions; + + public partial class Block8x8FTests : JpegFixture + { + public class CopyToBufferArea : JpegFixture + { + public CopyToBufferArea(ITestOutputHelper output) + : base(output) + { + } + + private static void VerifyAllZeroOutsideSubArea(Buffer2D buffer, int subX, int subY, int horizontalFactor = 1, int verticalFactor = 1) + { + for (int y = 0; y < 20; y++) + { + for (int x = 0; x < 20; x++) + { + if (x < subX || x >= subX + 8 * horizontalFactor || y < subY || y >= subY + 8 * verticalFactor) + { + Assert.Equal(0, buffer[x, y]); + } + } + } + } + + // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. + [Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] + //[Fact] + public void Unscaled() + { + Block8x8F block = CreateRandomFloatBlock(0, 100); + + using (var buffer = new Buffer2D(20, 20)) + { + BufferArea area = buffer.GetArea(5, 10, 8, 8); + block.CopyTo(area); + + Assert.Equal(block[0, 0], buffer[5, 10]); + Assert.Equal(block[1, 0], buffer[6, 10]); + Assert.Equal(block[0, 1], buffer[5, 11]); + Assert.Equal(block[0, 7], buffer[5, 17]); + Assert.Equal(block[63], buffer[12, 17]); + + VerifyAllZeroOutsideSubArea(buffer, 5, 10); + } + } + + // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. + [Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] + //[Theory] + [InlineData(1, 1)] + [InlineData(1, 2)] + [InlineData(2, 1)] + [InlineData(2, 2)] + [InlineData(4, 2)] + [InlineData(4, 4)] + public void Scaled(int horizontalFactor, int verticalFactor) + { + Block8x8F block = CreateRandomFloatBlock(0, 100); + + var start = new Point(50, 50); + + using (var buffer = new Buffer2D(100, 100)) + { + BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); + block.CopyTo(area, horizontalFactor, verticalFactor); + + for (int y = 0; y < 8 * verticalFactor; y++) + { + for (int x = 0; x < 8 * horizontalFactor; x++) + { + int yy = y / verticalFactor; + int xx = x / horizontalFactor; + + float expected = block[xx, yy]; + float actual = area[x, y]; + + Assert.Equal(expected, actual); + } + } + + VerifyAllZeroOutsideSubArea(buffer, start.X, start.Y, horizontalFactor, verticalFactor); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 01501a33d..52c38bee8 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -1,25 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // Uncomment this to turn unit tests into benchmarks: //#define BENCHMARKING // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { + using System; using System.Diagnostics; - using System.Numerics; - using ImageSharp.Formats; - using ImageSharp.Formats.Jpg; + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using Xunit; using Xunit.Abstractions; - public class Block8x8FTests : JpegUtilityTestFixture + public partial class Block8x8FTests : JpegFixture { #if BENCHMARKING public const int Times = 1000000; @@ -40,15 +40,15 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F block = new Block8x8F(); + var block = new Block8x8F(); - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { block[i] = i; } sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += block[i]; } @@ -64,15 +64,15 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F block = new Block8x8F(); + var block = new Block8x8F(); - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { Block8x8F.SetScalarAt(&block, i, i); } sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += Block8x8F.GetScalarAt(&block, i); } @@ -91,13 +91,13 @@ namespace ImageSharp.Tests { // Block8x8F block = new Block8x8F(); float[] block = new float[64]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { block[i] = i; } sum = 0; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { sum += block[i]; } @@ -108,10 +108,10 @@ namespace ImageSharp.Tests [Fact] public void Load_Store_FloatArray() { - float[] data = new float[Block8x8F.ScalarCount]; - float[] mirror = new float[Block8x8F.ScalarCount]; + float[] data = new float[Block8x8F.Size]; + float[] mirror = new float[Block8x8F.Size]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { data[i] = i; } @@ -120,23 +120,23 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F b = new Block8x8F(); + var b = new Block8x8F(); b.LoadFrom(data); b.CopyTo(mirror); }); Assert.Equal(data, mirror); - // PrintLinearData((MutableSpan)mirror); + // PrintLinearData((Span)mirror); } [Fact] public unsafe void Load_Store_FloatArray_Ptr() { - float[] data = new float[Block8x8F.ScalarCount]; - float[] mirror = new float[Block8x8F.ScalarCount]; + float[] data = new float[Block8x8F.Size]; + float[] mirror = new float[Block8x8F.Size]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { data[i] = i; } @@ -145,23 +145,23 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F b = new Block8x8F(); + var b = new Block8x8F(); Block8x8F.LoadFrom(&b, data); Block8x8F.CopyTo(&b, mirror); }); Assert.Equal(data, mirror); - // PrintLinearData((MutableSpan)mirror); + // PrintLinearData((Span)mirror); } [Fact] public void Load_Store_IntArray() { - int[] data = new int[Block8x8F.ScalarCount]; - int[] mirror = new int[Block8x8F.ScalarCount]; + int[] data = new int[Block8x8F.Size]; + int[] mirror = new int[Block8x8F.Size]; - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < Block8x8F.Size; i++) { data[i] = i; } @@ -170,14 +170,14 @@ namespace ImageSharp.Tests Times, () => { - Block8x8F v = new Block8x8F(); + var v = new Block8x8F(); v.LoadFrom(data); v.CopyTo(mirror); }); Assert.Equal(data, mirror); - // PrintLinearData((MutableSpan)mirror); + // PrintLinearData((Span)mirror); } [Fact] @@ -186,10 +186,10 @@ namespace ImageSharp.Tests float[] expected = Create8x8FloatData(); ReferenceImplementations.Transpose8x8(expected); - Block8x8F source = new Block8x8F(); + var source = new Block8x8F(); source.LoadFrom(Create8x8FloatData()); - Block8x8F dest = new Block8x8F(); + var dest = new Block8x8F(); source.TransposeInto(ref dest); float[] actual = new float[64]; @@ -206,12 +206,12 @@ namespace ImageSharp.Tests [Fact] public void TranposeInto_Benchmark() { - BufferHolder source = new BufferHolder(); + var source = new BufferHolder(); source.Buffer.LoadFrom(Create8x8FloatData()); - BufferHolder dest = new BufferHolder(); + var dest = new BufferHolder(); this.Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {Times} ..."); - Stopwatch sw = Stopwatch.StartNew(); + var sw = Stopwatch.StartNew(); for (int i = 0; i < Times; i++) { @@ -221,93 +221,12 @@ namespace ImageSharp.Tests sw.Stop(); this.Output.WriteLine($"TranposeInto_PinningImpl_Benchmark finished in {sw.ElapsedMilliseconds} ms"); } - - [Fact] - public void iDCT2D8x4_LeftPart() - { - float[] sourceArray = Create8x8FloatData(); - float[] expectedDestArray = new float[64]; - - ReferenceImplementations.iDCT2D8x4_32f(sourceArray, expectedDestArray); - - Block8x8F source = new Block8x8F(); - source.LoadFrom(sourceArray); - - Block8x8F dest = new Block8x8F(); - - DCT.IDCT8x4_LeftPart(ref source, ref dest); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); - - Assert.Equal(expectedDestArray, actualDestArray); - } - - [Fact] - public void iDCT2D8x4_RightPart() - { - MutableSpan sourceArray = Create8x8FloatData(); - MutableSpan expectedDestArray = new float[64]; - - ReferenceImplementations.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); - - Block8x8F source = new Block8x8F(); - source.LoadFrom(sourceArray); - - Block8x8F dest = new Block8x8F(); - - DCT.IDCT8x4_RightPart(ref source, ref dest); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); - - Assert.Equal(expectedDestArray.Data, actualDestArray); - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - public void TransformIDCT(int seed) - { - MutableSpan sourceArray = Create8x8RandomFloatData(-200, 200, seed); - float[] expectedDestArray = new float[64]; - float[] tempArray = new float[64]; - - ReferenceImplementations.iDCT2D_llm(sourceArray, expectedDestArray, tempArray); - - // ReferenceImplementations.iDCT8x8_llm_sse(sourceArray, expectedDestArray, tempArray); - Block8x8F source = new Block8x8F(); - source.LoadFrom(sourceArray); - - Block8x8F dest = new Block8x8F(); - Block8x8F tempBuffer = new Block8x8F(); - - DCT.TransformIDCT(ref source, ref dest, ref tempBuffer); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); - Assert.Equal(expectedDestArray, actualDestArray, new ApproximateFloatComparer(1f)); - Assert.Equal(expectedDestArray, actualDestArray, new ApproximateFloatComparer(1f)); - } - + [Fact] public unsafe void CopyColorsTo() { float[] data = Create8x8FloatData(); - Block8x8F block = new Block8x8F(); + var block = new Block8x8F(); block.LoadFrom(data); block.MultiplyAllInplace(5); @@ -318,11 +237,11 @@ namespace ImageSharp.Tests byte[] colorsExpected = new byte[stride * height]; byte[] colorsActual = new byte[stride * height]; - Block8x8F temp = new Block8x8F(); + var temp = new Block8x8F(); - ReferenceImplementations.CopyColorsTo(ref block, new MutableSpan(colorsExpected, offset), stride); + ReferenceImplementations.CopyColorsTo(ref block, new Span(colorsExpected, offset), stride); - block.CopyColorsTo(new MutableSpan(colorsActual, offset), stride, &temp); + block.CopyColorsTo(new Span(colorsActual, offset), stride, &temp); // Output.WriteLine("******* EXPECTED: *********"); // PrintLinearData(colorsExpected); @@ -344,17 +263,29 @@ namespace ImageSharp.Tests return result; } - [Fact] - public void TransformByteConvetibleColorValuesInto() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void NormalizeColors(bool inplace) { - Block8x8F block = new Block8x8F(); + var block = default(Block8x8F); float[] input = Create8x8ColorCropTestData(); block.LoadFrom(input); this.Output.WriteLine("Input:"); this.PrintLinearData(input); + + var dest = default(Block8x8F); - Block8x8F dest = new Block8x8F(); - block.TransformByteConvetibleColorValuesInto(ref dest); + if (inplace) + { + dest = block; + dest.NormalizeColorsInplace(); + } + else + { + block.NormalizeColorsInto(ref dest); + } + float[] array = new float[64]; dest.CopyTo(array); @@ -366,98 +297,75 @@ namespace ImageSharp.Tests } } + [Theory] [InlineData(1)] [InlineData(2)] - public void FDCT8x4_LeftPart(int seed) + public unsafe void UnzigDivRound(int seed) { - MutableSpan src = Create8x8RandomFloatData(-200, 200, seed); - Block8x8F srcBlock = new Block8x8F(); - srcBlock.LoadFrom(src); - - Block8x8F destBlock = new Block8x8F(); - - MutableSpan expectedDest = new MutableSpan(64); - - ReferenceImplementations.fDCT2D8x4_32f(src, expectedDest); - DCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); - - MutableSpan actualDest = new MutableSpan(64); - destBlock.CopyTo(actualDest); + var block = new Block8x8F(); + block.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); - Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); - } + var qt = new Block8x8F(); + qt.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); - [Theory] - [InlineData(1)] - [InlineData(2)] - public void FDCT8x4_RightPart(int seed) - { - MutableSpan src = Create8x8RandomFloatData(-200, 200, seed); - Block8x8F srcBlock = new Block8x8F(); - srcBlock.LoadFrom(src); + var unzig = UnzigData.Create(); - Block8x8F destBlock = new Block8x8F(); + int* expectedResults = stackalloc int[Block8x8F.Size]; + ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data); - MutableSpan expectedDest = new MutableSpan(64); + var actualResults = default(Block8x8F); - ReferenceImplementations.fDCT2D8x4_32f(src.Slice(4), expectedDest.Slice(4)); - DCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); + Block8x8F.UnzigDivRound(&block, &actualResults, &qt, unzig.Data); - MutableSpan actualDest = new MutableSpan(64); - destBlock.CopyTo(actualDest); + for (int i = 0; i < Block8x8F.Size; i++) + { + int expected = expectedResults[i]; + int actual = (int)actualResults[i]; - Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); + Assert.Equal(expected, actual); + } } - - [Theory] - [InlineData(1)] - [InlineData(2)] - public void TransformFDCT(int seed) + + [Fact] + public void RoundInto() { - MutableSpan src = Create8x8RandomFloatData(-200, 200, seed); - Block8x8F srcBlock = new Block8x8F(); - srcBlock.LoadFrom(src); - - Block8x8F destBlock = new Block8x8F(); + float[] data = Create8x8RandomFloatData(-1000, 1000); - MutableSpan expectedDest = new MutableSpan(64); - MutableSpan temp1 = new MutableSpan(64); - Block8x8F temp2 = new Block8x8F(); + var source = default(Block8x8F); + source.LoadFrom(data); + var dest = default(Block8x8); - ReferenceImplementations.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); - DCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); + source.RoundInto(ref dest); - MutableSpan actualDest = new MutableSpan(64); - destBlock.CopyTo(actualDest); + for (int i = 0; i < Block8x8.Size; i++) + { + float expectedFloat = data[i]; + short expectedShort = (short) Math.Round(expectedFloat); + short actualShort = dest[i]; - Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); + Assert.Equal(expectedShort, actualShort); + } } [Theory] [InlineData(1)] [InlineData(2)] - public unsafe void UnzigDivRound(int seed) + [InlineData(3)] + public void RoundInplace(int seed) { - Block8x8F block = new Block8x8F(); - block.LoadFrom(Create8x8RandomFloatData(-2000, 2000, seed)); - - Block8x8F qt = new Block8x8F(); - qt.LoadFrom(Create8x8RandomFloatData(-2000, 2000, seed)); + Block8x8F s = CreateRandomFloatBlock(-500, 500, seed); - UnzigData unzig = UnzigData.Create(); - - int* expectedResults = stackalloc int[Block8x8F.ScalarCount]; - ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data); + Block8x8F d = s; + d.RoundInplace(); - Block8x8F actualResults = default(Block8x8F); + this.Output.WriteLine(s.ToString()); + this.Output.WriteLine(d.ToString()); - Block8x8F.UnzigDivRound(&block, &actualResults, &qt, unzig.Data); - - for (int i = 0; i < Block8x8F.ScalarCount; i++) + for (int i = 0; i < 64; i++) { - int expected = expectedResults[i]; - int actual = (int)actualResults[i]; + float expected = MathF.Round(s[i]); + float actual = d[i]; Assert.Equal(expected, actual); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs new file mode 100644 index 000000000..c2fa8c8d4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs @@ -0,0 +1,143 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public class Block8x8Tests : JpegFixture + { + public Block8x8Tests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void Construct_And_Indexer_Get() + { + short[] data = Create8x8ShortData(); + + var block = new Block8x8(data); + + for (int i = 0; i < Block8x8.Size; i++) + { + Assert.Equal(data[i], block[i]); + } + } + + [Fact] + public void Indexer_Set() + { + var block = default(Block8x8); + + block[17] = 17; + block[42] = 42; + + Assert.Equal(0, block[0]); + Assert.Equal(17, block[17]); + Assert.Equal(42, block[42]); + } + + + [Fact] + public unsafe void Indexer_GetScalarAt_SetScalarAt() + { + int sum = 0; + var block = default(Block8x8); + + for (int i = 0; i < Block8x8.Size; i++) + { + Block8x8.SetScalarAt(&block, i, (short)i); + } + + sum = 0; + for (int i = 0; i < Block8x8.Size; i++) + { + sum += Block8x8.GetScalarAt(&block, i); + } + Assert.Equal(sum, 64 * 63 / 2); + } + + + [Fact] + public void AsFloatBlock() + { + short[] data = Create8x8ShortData(); + + var source = new Block8x8(data); + + Block8x8F dest = source.AsFloatBlock(); + + for (int i = 0; i < Block8x8F.Size; i++) + { + Assert.Equal((float)data[i], dest[i]); + } + } + + [Fact] + public void ToArray() + { + short[] data = Create8x8ShortData(); + var block = new Block8x8(data); + + short[] result = block.ToArray(); + + Assert.Equal(data, result); + } + + [Fact] + public void Equality_WhenTrue() + { + short[] data = Create8x8ShortData(); + var block1 = new Block8x8(data); + var block2 = new Block8x8(data); + + block1[0] = 42; + block2[0] = 42; + + Assert.Equal(block1, block2); + Assert.Equal(block1.GetHashCode(), block2.GetHashCode()); + } + + [Fact] + public void Equality_WhenFalse() + { + short[] data = Create8x8ShortData(); + var block1 = new Block8x8(data); + var block2 = new Block8x8(data); + + block1[0] = 42; + block2[0] = 666; + + Assert.NotEqual(block1, block2); + } + + [Fact] + public void IndexerXY() + { + var block = default(Block8x8); + block[8 * 3 + 5] = 42; + + short value = block[5, 3]; + + Assert.Equal(42, value); + } + + [Fact] + public void TotalDifference() + { + short[] data = Create8x8ShortData(); + var block1 = new Block8x8(data); + var block2 = new Block8x8(data); + + block2[10] += 7; + block2[63] += 8; + + long d = Block8x8.TotalDifference(ref block1, ref block2); + + Assert.Equal(15, d); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs new file mode 100644 index 000000000..ee6f5305f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -0,0 +1,182 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public static class DCTTests + { + public class FastFloatingPoint : JpegFixture + { + public FastFloatingPoint(ITestOutputHelper output) + : base(output) + { + } + + + [Fact] + public void iDCT2D8x4_LeftPart() + { + float[] sourceArray = JpegFixture.Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); + + Block8x8F source = new Block8x8F(); + source.LoadFrom(sourceArray); + + Block8x8F dest = new Block8x8F(); + + FastFloatingPointDCT.IDCT8x4_LeftPart(ref source, ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + this.Print8x8Data(expectedDestArray); + this.Output.WriteLine("**************"); + this.Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray, actualDestArray); + } + + [Fact] + public void iDCT2D8x4_RightPart() + { + float[] sourceArray = JpegFixture.Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray.AsSpan().Slice(4), expectedDestArray.AsSpan().Slice(4)); + + Block8x8F source = new Block8x8F(); + source.LoadFrom(sourceArray); + + Block8x8F dest = new Block8x8F(); + + FastFloatingPointDCT.IDCT8x4_RightPart(ref source, ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + this.Print8x8Data(expectedDestArray); + this.Output.WriteLine("**************"); + this.Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray, actualDestArray); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void LLM_TransformIDCT_CompareToNonOptimized(int seed) + { + float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); + + var temp = default(Block8x8F); + var actual = default(Block8x8F); + FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); + + this.CompareBlocks(expected, actual, 1f); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void LLM_TransformIDCT_CompareToAccurate(int seed) + { + float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-1000, 1000, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + + var temp = default(Block8x8F); + var actual = default(Block8x8F); + FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); + + this.CompareBlocks(expected, actual, 1f); + } + + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void FDCT8x4_LeftPart(int seed) + { + Span src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); + Block8x8F srcBlock = new Block8x8F(); + srcBlock.LoadFrom(src); + + Block8x8F destBlock = new Block8x8F(); + + float[] expectedDest = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src, expectedDest); + FastFloatingPointDCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); + + float[] actualDest = new float[64]; + destBlock.CopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void FDCT8x4_RightPart(int seed) + { + Span src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); + Block8x8F srcBlock = new Block8x8F(); + srcBlock.LoadFrom(src); + + Block8x8F destBlock = new Block8x8F(); + + float[] expectedDest = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan().Slice(4)); + FastFloatingPointDCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); + + float[] actualDest = new float[64]; + destBlock.CopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void TransformFDCT(int seed) + { + Span src = JpegFixture.Create8x8RoundedRandomFloatData(-200, 200, seed); + Block8x8F srcBlock = new Block8x8F(); + srcBlock.LoadFrom(src); + + Block8x8F destBlock = new Block8x8F(); + + float[] expectedDest = new float[64]; + float[] temp1 = new float[64]; + Block8x8F temp2 = new Block8x8F(); + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); + FastFloatingPointDCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); + + float[] actualDest = new float[64]; + destBlock.CopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs new file mode 100644 index 000000000..706fa1e20 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -0,0 +1,304 @@ +using System; +using System.Numerics; + +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Memory; + +using Xunit; +using Xunit.Abstractions; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + public class JpegColorConverterTests + { + private const float Precision = 1/255f; + + public static readonly TheoryData CommonConversionData = + new TheoryData + { + { 40, 40, 1 }, + { 42, 40, 2 }, + { 42, 39, 3 } + }; + + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + + public JpegColorConverterTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromYCbCrBasic(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion(new JpegColorConverter.FromYCbCrBasic(), 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + } + + private static void ValidateYCbCr(JpegColorConverter.ComponentValues values, Span result, int i) + { + float y = values.Component0[i]; + float cb = values.Component1[i]; + float cr = values.Component2[i]; + var ycbcr = new YCbCr(y, cb, cr); + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = ColorSpaceConverter.ToRgb(ycbcr); + + Assert.True(actual.AlmostEquals(expected, Precision), $"{actual} != {expected}"); + Assert.Equal(1, rgba.W); + } + + [Theory] + [InlineData(64, 1)] + [InlineData(16, 2)] + [InlineData(8, 3)] + public void FromYCbCrSimd_ConvertCore(int size, int seed) + { + ValidateConversion(JpegColorConverter.FromYCbCrSimd.ConvertCore, 3, size, size, seed, ValidateYCbCr); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void FromYCbCrSimd(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion(new JpegColorConverter.FromYCbCrSimd(), 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromYCbCr_WithDefaultConverter(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion(JpegColorSpace.YCbCr, 3, inputBufferLength, resultBufferLength, seed, ValidateYCbCr); + } + + // Becnhmark, for local execution only + //[Theory] + //[InlineData(false)] + //[InlineData(true)] + public void BenchmarkYCbCr(bool simd) + { + int count = 2053; + int times = 50000; + + JpegColorConverter.ComponentValues values = CreateRandomValues(3, count, 1); + Vector4[] result = new Vector4[count]; + + JpegColorConverter converter = simd ? (JpegColorConverter)new JpegColorConverter.FromYCbCrSimd() : new JpegColorConverter.FromYCbCrBasic(); + + // Warm up: + converter.ConvertToRGBA(values, result); + + using (new MeasureGuard(this.Output, $"{converter.GetType().Name} x {times}")) + { + for (int i = 0; i < times; i++) + { + converter.ConvertToRGBA(values, result); + } + } + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromCmyk(int inputBufferLength, int resultBufferLength, int seed) + { + var v = new Vector4(0, 0, 0, 1F); + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + ValidateConversion( + JpegColorSpace.Cmyk, + 4, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float c = values.Component0[i]; + float m = values.Component1[i]; + float y = values.Component2[i]; + float k = values.Component3[i] / 255F; + + v.X = c * k; + v.Y = m * k; + v.Z = y * k; + v.W = 1F; + + v *= scale; + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(v.X, v.Y, v.Z); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromGrayScale(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion( + JpegColorSpace.GrayScale, + 1, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float y = values.Component0[i]; + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(y / 255F, y / 255F, y / 255F); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromRgb(int inputBufferLength, int resultBufferLength, int seed) + { + ValidateConversion( + JpegColorSpace.RGB, + 3, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float r = values.Component0[i]; + float g = values.Component1[i]; + float b = values.Component2[i]; + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(r / 255F, g / 255F, b / 255F); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + [Theory] + [MemberData(nameof(CommonConversionData))] + public void ConvertFromYcck(int inputBufferLength, int resultBufferLength, int seed) + { + var v = new Vector4(0, 0, 0, 1F); + var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + + ValidateConversion( + JpegColorSpace.Ycck, + 4, + inputBufferLength, + resultBufferLength, + seed, + (values, result, i) => + { + float y = values.Component0[i]; + float cb = values.Component1[i] - 128F; + float cr = values.Component2[i] - 128F; + float k = values.Component3[i] / 255F; + + v.X = (255F - MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero)) * k; + v.Y = (255F - MathF.Round( + y - (0.344136F * cb) - (0.714136F * cr), + MidpointRounding.AwayFromZero)) * k; + v.Z = (255F - MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero)) * k; + v.W = 1F; + + v *= scale; + + Vector4 rgba = result[i]; + var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); + var expected = new Rgb(v.X, v.Y, v.Z); + + Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(1, rgba.W); + }); + } + + private static JpegColorConverter.ComponentValues CreateRandomValues( + int componentCount, + int inputBufferLength, + int seed, + float minVal = 0f, + float maxVal = 255f) + { + var rnd = new Random(seed); + Buffer2D[] buffers = new Buffer2D[componentCount]; + for (int i = 0; i < componentCount; i++) + { + float[] values = new float[inputBufferLength]; + + for (int j = 0; j < inputBufferLength; j++) + { + values[j] = (float)rnd.NextDouble() * (maxVal-minVal)+minVal; + } + + // no need to dispose when buffer is not array owner + buffers[i] = new Buffer2D(values, values.Length, 1); + } + return new JpegColorConverter.ComponentValues(buffers, 0); + } + + private static void ValidateConversion( + JpegColorSpace colorSpace, + int componentCount, + int inputBufferLength, + int resultBufferLength, + int seed, + Action, int> validatePixelValue) + { + ValidateConversion( + JpegColorConverter.GetConverter(colorSpace), + componentCount, + inputBufferLength, + resultBufferLength, + seed, + validatePixelValue); + } + + private static void ValidateConversion( + JpegColorConverter converter, + int componentCount, + int inputBufferLength, + int resultBufferLength, + int seed, + Action, int> validatePixelValue) + { + ValidateConversion( + converter.ConvertToRGBA, + componentCount, + inputBufferLength, + resultBufferLength, + seed, + validatePixelValue); + } + + private static void ValidateConversion( + Action> doConvert, + int componentCount, + int inputBufferLength, + int resultBufferLength, + int seed, + Action, int> validatePixelValue) + { + JpegColorConverter.ComponentValues values = CreateRandomValues(componentCount, inputBufferLength, seed); + Vector4[] result = new Vector4[resultBufferLength]; + + doConvert(values, result); + + for (int i = 0; i < resultBufferLength; i++) + { + validatePixelValue(values, result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 9401d098d..d8d24224b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -1,59 +1,241 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests + +using System; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using System; using System.IO; + using System.Linq; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.Primitives; using Xunit; + using Xunit.Abstractions; - public class JpegDecoderTests : TestBase + public class JpegDecoderTests { public static string[] BaselineTestJpegs = { - TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, - TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Jpeg444, - TestImages.Jpeg.Baseline.Testimgorig + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Testorig420, + + // BUG: The following image has a high difference compared to the expected output: + //TestImages.Jpeg.Baseline.Jpeg420Small, + + TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.Bad.ExifUndefType, + }; + + public static string[] ProgressiveTestJpegs = + { + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF, + TestImages.Jpeg.Issues.BadCoeffsProgressive178, + TestImages.Jpeg.Issues.MissingFF00ProgressiveGirl159, }; - public static string[] ProgressiveTestJpegs = TestImages.Jpeg.Progressive.All; + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector; + + // TODO: We should make this comparer less tolerant ... + private static readonly ImageComparer VeryTolerantJpegComparer = + ImageComparer.Tolerant(0.005f, perPixelManhattanThreshold: 4); + + // BUG: PDF.js output is wrong on spectral level! + private static readonly ImageComparer PdfJsProgressiveComparer = + ImageComparer.Tolerant(0.015f, perPixelManhattanThreshold: 4); + + + public JpegDecoderTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + private static IImageDecoder OrigJpegDecoder => new OrigJpegDecoder(); + + private static IImageDecoder PdfJsJpegDecoder => new PdfJsJpegDecoder(); + + [Fact] + public void ParseStream_BasicPropertiesAreCorrect1_PdfJs() + { + byte[] bytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; + using (var ms = new MemoryStream(bytes)) + { + var decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + decoder.ParseStream(ms); + + VerifyJpeg.VerifyComponentSizes3(decoder.Frame.Components, 43, 61, 22, 31, 22, 31); + } + } + + public const string DecodeBaselineJpegOutputName = "DecodeBaselineJpeg"; [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32)] - public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] + public void DecodeBaselineJpeg_PdfJs(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage(PdfJsJpegDecoder)) { - provider.Utility.SaveTestOutputFile(image, "bmp"); + image.DebugSave(provider); + + provider.Utility.TestName = DecodeBaselineJpegOutputName; + image.CompareToReferenceOutput(provider, VeryTolerantJpegComparer, appendPixelTypeToFileName: false); } } [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32)] - public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) + [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, false)] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, CommonNonDefaultPixelTypes, true)] + public void JpegDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider, bool useOldDecoder) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + IImageDecoder decoder = useOldDecoder ? OrigJpegDecoder : PdfJsJpegDecoder; + using (Image image = provider.GetImage(decoder)) + { + image.DebugSave(provider); + + provider.Utility.TestName = DecodeBaselineJpegOutputName; + image.CompareToReferenceOutput(provider, VeryTolerantJpegComparer, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] + public void DecodeBaselineJpeg_Orig(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(OrigJpegDecoder)) + { + image.DebugSave(provider); + + provider.Utility.TestName = DecodeBaselineJpegOutputName; + image.CompareToReferenceOutput(provider, VeryTolerantJpegComparer, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithFile(TestImages.Jpeg.Issues.CriticalEOF214, PixelTypes.Rgba32)] + public void DecodeBaselineJpeg_CriticalEOF_ShouldThrow_Orig(TestImageProvider provider) + where TPixel : struct, IPixel + { + // TODO: We need a public ImageDecoderException class in ImageSharp! + Assert.ThrowsAny(() => provider.GetImage(OrigJpegDecoder)); + } + + public const string DecodeProgressiveJpegOutputName = "DecodeProgressiveJpeg"; + + [Theory] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] + public void DecodeProgressiveJpeg_PdfJs(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(PdfJsJpegDecoder)) + { + image.DebugSave(provider); + + provider.Utility.TestName = DecodeProgressiveJpegOutputName; + image.CompareToReferenceOutput(provider, PdfJsProgressiveComparer, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] + public void DecodeProgressiveJpeg_Orig(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(OrigJpegDecoder)) + { + image.DebugSave(provider); + + provider.Utility.TestName = DecodeProgressiveJpegOutputName; + image.CompareToReferenceOutput(provider, VeryTolerantJpegComparer, appendPixelTypeToFileName: false); + } + } + + private float GetDifferenceInPercents(Image image, TestImageProvider provider) + where TPixel : struct, IPixel + { + var reportingComparer = ImageComparer.Tolerant(0, 0); + + ImageSimilarityReport report = image.GetReferenceOutputSimilarityReports( + provider, + reportingComparer, + appendPixelTypeToFileName: false + ).SingleOrDefault(); + + if (report != null && report.TotalNormalizedDifference.HasValue) { - provider.Utility.SaveTestOutputFile(image, "bmp"); + return report.TotalNormalizedDifference.Value * 100; + } + + return 0; + } + + private void CompareJpegDecodersImpl(TestImageProvider provider, string testName) + where TPixel : struct, IPixel + { + if (TestEnvironment.RunsOnCI) // Debug only test + { + return; + } + + this.Output.WriteLine(provider.SourceFileOrDescription); + provider.Utility.TestName = testName; + + using (Image image = provider.GetImage(OrigJpegDecoder)) + { + double d = this.GetDifferenceInPercents(image, provider); + this.Output.WriteLine($"Difference using ORIGINAL decoder: {d:0.0000}%"); + } + + using (Image image = provider.GetImage(PdfJsJpegDecoder)) + { + double d = this.GetDifferenceInPercents(image, provider); + this.Output.WriteLine($"Difference using PDFJS decoder: {d:0.0000}%"); } } + [Theory] + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32)] + public void CompareJpegDecoders_Baseline(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.CompareJpegDecodersImpl(provider, DecodeBaselineJpegOutputName); + } + + [Theory] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)] + public void CompareJpegDecoders_Progressive(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.CompareJpegDecodersImpl(provider, DecodeProgressiveJpegOutputName); + } + [Theory] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio420, 75)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio420, 100)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 75)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 100)] [WithSolidFilledImages(8, 8, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 100)] - public void DecodeGenerated_SaveBmp( + public void DecodeGenerated_Orig( TestImageProvider provider, JpegSubsample subsample, int quality) @@ -62,45 +244,19 @@ namespace ImageSharp.Tests byte[] data; using (Image image = provider.GetImage()) { - JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; + var encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; data = new byte[65536]; - using (MemoryStream ms = new MemoryStream(data)) + using (var ms = new MemoryStream(data)) { image.Save(ms, encoder); } } - // TODO: Automatic image comparers could help here a lot :P - Image mirror = provider.Factory.CreateImage(data); - provider.Utility.TestName += $"_{subsample}_Q{quality}"; - provider.Utility.SaveTestOutputFile(mirror, "bmp"); - } - - [Theory] - [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.Rgba32)] - public void DecodeGenerated_MetadataOnly( - TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - using (MemoryStream ms = new MemoryStream()) - { - image.Save(ms, new JpegEncoder()); - ms.Seek(0, SeekOrigin.Begin); - - using (JpegDecoderCore decoder = new JpegDecoderCore(null, new JpegDecoder())) - { - Image mirror = decoder.Decode(ms); - - Assert.Equal(decoder.ImageWidth, image.Width); - Assert.Equal(decoder.ImageHeight, image.Height); - } - } - } + var mirror = Image.Load(data, OrigJpegDecoder); + mirror.DebugSave(provider, $"_{subsample}_Q{quality}"); } - + [Fact] public void Decoder_Reads_Correct_Resolution_From_Jfif() { @@ -114,7 +270,7 @@ namespace ImageSharp.Tests [Fact] public void Decoder_Reads_Correct_Resolution_From_Exif() { - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420Exif).CreateImage()) { Assert.Equal(72, image.MetaData.HorizontalResolution); Assert.Equal(72, image.MetaData.VerticalResolution); @@ -124,12 +280,12 @@ namespace ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsFalse_ExifProfileIsRead() { - JpegDecoder decoder = new JpegDecoder() + var decoder = new JpegDecoder() { IgnoreMetadata = false }; - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); using (Image image = testFile.CreateImage(decoder)) { @@ -140,17 +296,49 @@ namespace ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored() { - JpegDecoder options = new JpegDecoder() + var options = new JpegDecoder() { IgnoreMetadata = true }; - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); using (Image image = testFile.CreateImage(options)) { Assert.Null(image.MetaData.ExifProfile); } } + + // DEBUG ONLY! + // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" + // into "\tests\Images\ActualOutput\JpegDecoderTests\" + //[Theory] + //[WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32, "PdfJsOriginal_progress.png")] + public void ValidateProgressivePdfJsOutput(TestImageProvider provider, + string pdfJsOriginalResultImage) + where TPixel : struct, IPixel + { + // tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm + string pdfJsOriginalResultPath = Path.Combine( + provider.Utility.GetTestOutputDir(), + pdfJsOriginalResultImage); + + byte[] sourceBytes = TestFile.Create(TestImages.Jpeg.Progressive.Progress).Bytes; + + provider.Utility.TestName = nameof(DecodeProgressiveJpegOutputName); + + var comparer = ImageComparer.Tolerant(0, 0); + + using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) + using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) + using (var pdfJsPortResult = Image.Load(sourceBytes, PdfJsJpegDecoder)) + { + ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); + ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); + + this.Output.WriteLine($"Difference for PDF.js ORIGINAL: {originalReport.DifferencePercentageString}"); + this.Output.WriteLine($"Difference for PORT: {portReport.DifferencePercentageString}"); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 86faeee23..3bd1ed265 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -1,24 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using ImageSharp.Formats; -using Xunit; -using Xunit.Abstractions; + + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using ImageSharp.Formats.Jpg; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; + using System.Collections.Generic; + using System.IO; + + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; using SixLabors.Primitives; + using Xunit; + using Xunit.Abstractions; + public class JpegEncoderTests : MeasureFixture { public static IEnumerable AllBmpFiles => TestImages.Bmp.All; @@ -36,8 +35,9 @@ namespace ImageSharp.Tests public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) where TPixel : struct, IPixel { - using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) + using (Image image = provider.GetImage(x=>x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) { + image.MetaData.ExifProfile = null; // Reduce the size of the file JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs new file mode 100644 index 000000000..df6d1ef1b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -0,0 +1,107 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Xunit; + using Xunit.Abstractions; + + public class JpegImagePostProcessorTests + { + public static string[] BaselineTestJpegs = + { + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, + TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.Bad.ExifUndefType, + }; + + public static string[] ProgressiveTestJpegs = + { + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + }; + + public JpegImagePostProcessorTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + private static void SaveBuffer(JpegComponentPostProcessor cp, TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = cp.ColorBuffer.ToGrayscaleImage(1f / 255f)) + { + image.DebugSave(provider, $"-C{cp.Component.Index}-"); + } + + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)] + public void DoProcessorStep(TestImageProvider provider) + where TPixel : struct, IPixel + { + string imageFile = provider.SourceFileOrDescription; + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) + using (var pp = new JpegImagePostProcessor(decoder)) + using (var imageFrame = new ImageFrame(decoder.ImageWidth, decoder.ImageHeight)) + { + pp.DoPostProcessorStep(imageFrame); + + JpegComponentPostProcessor[] cp = pp.ComponentProcessors; + + SaveBuffer(cp[0], provider); + SaveBuffer(cp[1], provider); + SaveBuffer(cp[2], provider); + } + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Baseline.Jpeg444, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Baseline.Testorig420, PixelTypes.Rgba32)] + public void PostProcess(TestImageProvider provider) + where TPixel : struct, IPixel + { + string imageFile = provider.SourceFileOrDescription; + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile)) + using (var pp = new JpegImagePostProcessor(decoder)) + using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) + { + pp.PostProcess(image.Frames.RootFrame); + + image.DebugSave(provider); + + ImagingTestCaseUtility testUtil = provider.Utility; + testUtil.TestGroupName = nameof(JpegDecoderTests); + testUtil.TestName = JpegDecoderTests.DecodeBaselineJpegOutputName; + + using (Image referenceImage = + provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) + { + ImageSimilarityReport report = ImageComparer.Exact.CompareImagesOrFrames(referenceImage, image); + + this.Output.WriteLine($"*** {imageFile} ***"); + this.Output.WriteLine($"Difference: "+ report.DifferencePercentageString); + + // ReSharper disable once PossibleInvalidOperationException + Assert.True(report.TotalNormalizedDifference.Value < 0.005f); + } + } + + + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index daf8da6a3..063f42c00 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { using System; using System.IO; using System.Linq; using System.Numerics; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; using Xunit; using Xunit.Abstractions; @@ -29,13 +30,25 @@ namespace ImageSharp.Tests TestImages.Jpeg.Baseline.Ycck, TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Jpeg400, - TestImages.Jpeg.Baseline.Jpeg420, + TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Jpeg444, }; - // [Theory] // Benchmark, enable manually - // [MemberData(nameof(DecodeJpegData))] - public void DecodeJpeg(string fileName) + [Theory] // Benchmark, enable manually + [MemberData(nameof(DecodeJpegData))] + public void DecodeJpeg_Original(string fileName) + { + this.DecodeJpegBenchmarkImpl(fileName, new OrigJpegDecoder()); + } + + [Theory] // Benchmark, enable manually + [MemberData(nameof(DecodeJpegData))] + public void DecodeJpeg_PdfJs(string fileName) + { + this.DecodeJpegBenchmarkImpl(fileName, new PdfJsJpegDecoder()); + } + + private void DecodeJpegBenchmarkImpl(string fileName, IImageDecoder decoder) { const int ExecutionCount = 30; @@ -44,18 +57,17 @@ namespace ImageSharp.Tests throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); } - string path = TestFile.GetPath(fileName); + string path = TestFile.GetInputFileFullPath(fileName); byte[] bytes = File.ReadAllBytes(path); this.Measure( ExecutionCount, () => { - Image img = Image.Load(bytes); + Image img = Image.Load(bytes, decoder); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); - } // Benchmark, enable manually! diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs deleted file mode 100644 index 252b01138..000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs +++ /dev/null @@ -1,107 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -using System.Text; - -using Xunit.Abstractions; -// ReSharper disable InconsistentNaming - -namespace ImageSharp.Tests -{ - using System; - using System.Diagnostics; - - using ImageSharp.Formats.Jpg; - - public class JpegUtilityTestFixture : MeasureFixture - { - public JpegUtilityTestFixture(ITestOutputHelper output) : base(output) - { - } - - // ReSharper disable once InconsistentNaming - public static float[] Create8x8FloatData() - { - float[] result = new float[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i * 8 + j] = i * 10 + j; - } - } - return result; - } - - // ReSharper disable once InconsistentNaming - public static int[] Create8x8IntData() - { - int[] result = new int[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i * 8 + j] = i * 10 + j; - } - } - return result; - } - - // ReSharper disable once InconsistentNaming - public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) - { - Random rnd = new Random(seed); - int[] result = new int[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i * 8 + j] = rnd.Next(minValue, maxValue); - } - } - return result; - } - - internal static MutableSpan Create8x8RandomFloatData(int minValue, int maxValue, int seed = 42) - => new MutableSpan(Create8x8RandomIntData(minValue, maxValue, seed)).ConvertToFloat32MutableSpan(); - - internal void Print8x8Data(MutableSpan data) => this.Print8x8Data(data.Data); - - internal void Print8x8Data(T[] data) - { - StringBuilder bld = new StringBuilder(); - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - bld.Append($"{data[i * 8 + j],3} "); - } - bld.AppendLine(); - } - - this.Output.WriteLine(bld.ToString()); - } - - internal void PrintLinearData(T[] data) => this.PrintLinearData(new MutableSpan(data), data.Length); - - internal void PrintLinearData(MutableSpan data, int count = -1) - { - if (count < 0) count = data.TotalCount; - - StringBuilder bld = new StringBuilder(); - for (int i = 0; i < count; i++) - { - bld.Append($"{data[i],3} "); - } - this.Output.WriteLine(bld.ToString()); - } - - protected void Print(string msg) - { - Debug.WriteLine(msg); - this.Output.WriteLine(msg); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index f681e1d8f..887e9d7e9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -1,34 +1,33 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using System; using System.Numerics; - using ImageSharp.Formats.Jpg; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; + using SixLabors.ImageSharp.PixelFormats; using Xunit; - public class JpegUtilsTests : TestBase + public class JpegUtilsTests { - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + var image = new Image(10, 10); using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { - Vector4 v = new Vector4(i / 10f, j / 10f, 0, 1); + var v = new Vector4(i / 10f, j / 10f, 0, 1); - TPixel color = default(TPixel); + var color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -45,7 +44,7 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { using (Image src = provider.GetImage()) - using (Image dest = provider.Factory.CreateImage(8, 8)) + using (Image dest = new Image(8,8)) using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) using (PixelAccessor s = src.Lock()) using (PixelAccessor d = dest.Lock()) @@ -68,7 +67,7 @@ namespace ImageSharp.Tests { using (Image src = provider.GetImage()) using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) - using (Image dest = provider.Factory.CreateImage(8, 8)) + using (Image dest = new Image(8, 8)) using (PixelAccessor s = src.Lock()) using (PixelAccessor d = dest.Lock()) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/LibJpegToolsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/LibJpegToolsTests.cs new file mode 100644 index 000000000..773d7112b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/LibJpegToolsTests.cs @@ -0,0 +1,52 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System.IO; + + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + + public class LibJpegToolsTests + { + [Fact] + public void RunDumpJpegCoeffsTool() + { + if (!TestEnvironment.IsWindows) return; + + string inputFile = TestFile.GetInputFileFullPath(TestImages.Jpeg.Progressive.Progress); + string outputDir = TestEnvironment.CreateOutputDirectory(nameof(SpectralJpegTests)); + string outputFile = Path.Combine(outputDir, "progress.dctdump"); + + LibJpegTools.RunDumpJpegCoeffsTool(inputFile, outputFile); + + Assert.True(File.Exists(outputFile)); + } + + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Calliphora, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32)] + public void ExtractSpectralData(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.IsWindows) + { + return; + } + + string testImage = provider.SourceFileOrDescription; + LibJpegTools.SpectralData data = LibJpegTools.ExtractSpectralData(testImage); + + Assert.True(data.ComponentCount == 3); + Assert.True(data.Components.Length == 3); + + VerifyJpeg.SaveSpectralImage(provider, data); + + // I knew this one well: + if (testImage == TestImages.Jpeg.Progressive.Progress) + { + VerifyJpeg.VerifyComponentSizes3(data.Components, 43, 61, 22, 31, 22, 31); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs new file mode 100644 index 000000000..e56e91207 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ParseStreamTests.cs @@ -0,0 +1,134 @@ +using System; + +using SixLabors.ImageSharp.Formats.Jpeg.Common; +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; +using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; +using SixLabors.Primitives; +using Xunit; +using Xunit.Abstractions; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System.Text; + + public class ParseStreamTests + { + private ITestOutputHelper Output { get; } + + public ParseStreamTests(ITestOutputHelper output) + { + this.Output = output; + } + + [Theory] + [InlineData(TestImages.Jpeg.Baseline.Testorig420, JpegColorSpace.YCbCr)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg400, JpegColorSpace.GrayScale)] + [InlineData(TestImages.Jpeg.Baseline.Ycck, JpegColorSpace.Ycck)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk, JpegColorSpace.Cmyk)] + public void ColorSpace_IsDeducedCorrectly(string imageFile, object expectedColorSpaceValue) + { + var expecteColorSpace = (JpegColorSpace)expectedColorSpaceValue; + + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, true)) + { + Assert.Equal(expecteColorSpace, decoder.ColorSpace); + } + } + + [Fact] + public void ComponentScalingIsCorrect_1ChannelJpeg() + { + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(TestImages.Jpeg.Baseline.Jpeg400, true)) + { + Assert.Equal(1, decoder.ComponentCount); + Assert.Equal(1, decoder.Components.Length); + + Size expectedSizeInBlocks = decoder.ImageSizeInPixels.DivideRoundUp(8); + + Assert.Equal(expectedSizeInBlocks, decoder.ImageSizeInMCU); + + var uniform1 = new Size(1, 1); + OrigComponent c0 = decoder.Components[0]; + VerifyJpeg.VerifyComponent(c0, expectedSizeInBlocks, uniform1, uniform1); + } + } + + [Theory] + [InlineData(TestImages.Jpeg.Baseline.Jpeg444)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg420Small)] + [InlineData(TestImages.Jpeg.Baseline.Testorig420)] + [InlineData(TestImages.Jpeg.Baseline.Ycck)] + [InlineData(TestImages.Jpeg.Baseline.Cmyk)] + public void PrintComponentData(string imageFile) + { + StringBuilder bld = new StringBuilder(); + + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, true)) + { + bld.AppendLine(imageFile); + bld.AppendLine($"Size:{decoder.ImageSizeInPixels} MCU:{decoder.ImageSizeInMCU}"); + OrigComponent c0 = decoder.Components[0]; + OrigComponent c1 = decoder.Components[1]; + + bld.AppendLine($"Luma: SAMP: {c0.SamplingFactors} BLOCKS: {c0.SizeInBlocks}"); + bld.AppendLine($"Chroma: {c1.SamplingFactors} BLOCKS: {c1.SizeInBlocks}"); + } + this.Output.WriteLine(bld.ToString()); + } + + public static readonly TheoryData ComponentVerificationData = new TheoryData() + { + { TestImages.Jpeg.Baseline.Jpeg444, 3, new Size(1, 1), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Jpeg420Exif, 3, new Size(2, 2), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Jpeg420Small, 3, new Size(2, 2), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Testorig420, 3, new Size(2, 2), new Size(1, 1) }, + // TODO: Find Ycck or Cmyk images with different subsampling + { TestImages.Jpeg.Baseline.Ycck, 4, new Size(1, 1), new Size(1, 1) }, + { TestImages.Jpeg.Baseline.Cmyk, 4, new Size(1, 1), new Size(1, 1) }, + }; + + [Theory] + [MemberData(nameof(ComponentVerificationData))] + public void ComponentScalingIsCorrect_MultiChannelJpeg( + string imageFile, + int componentCount, + object expectedLumaFactors, + object expectedChromaFactors) + { + Size fLuma = (Size)expectedLumaFactors; + Size fChroma = (Size)expectedChromaFactors; + + using (OrigJpegDecoderCore decoder = JpegFixture.ParseStream(imageFile, true)) + { + Assert.Equal(componentCount, decoder.ComponentCount); + Assert.Equal(componentCount, decoder.Components.Length); + + OrigComponent c0 = decoder.Components[0]; + OrigComponent c1 = decoder.Components[1]; + OrigComponent c2 = decoder.Components[2]; + + var uniform1 = new Size(1, 1); + + Size expectedLumaSizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(fLuma) ; + + Size divisor = fLuma.DivideBy(fChroma); + + Size expectedChromaSizeInBlocks = expectedLumaSizeInBlocks.DivideRoundUp(divisor); + + VerifyJpeg.VerifyComponent(c0, expectedLumaSizeInBlocks, fLuma, uniform1); + VerifyJpeg.VerifyComponent(c1, expectedChromaSizeInBlocks, fChroma, divisor); + VerifyJpeg.VerifyComponent(c2, expectedChromaSizeInBlocks, fChroma, divisor); + + if (componentCount == 4) + { + OrigComponent c3 = decoder.Components[2]; + VerifyJpeg.VerifyComponent(c3, expectedLumaSizeInBlocks, fLuma, uniform1); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs deleted file mode 100644 index e76a11cec..000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs +++ /dev/null @@ -1,919 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -// ReSharper disable InconsistentNaming - -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.Formats; - using ImageSharp.Formats.Jpg; - - /// - /// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests - /// Floating point DCT code Ported from https://github.com/norishigefukushima/dct_simd - /// - internal static class ReferenceImplementations - { - /// - /// Transpose 8x8 block stored linearly in a (inplace) - /// - /// - internal static void Transpose8x8(MutableSpan data) - { - for (int i = 1; i < 8; i++) - { - int i8 = i * 8; - for (int j = 0; j < i; j++) - { - float tmp = data[i8 + j]; - data[i8 + j] = data[j * 8 + i]; - data[j * 8 + i] = tmp; - } - } - } - - /// - /// Transpose 8x8 block stored linearly in a - /// - internal static void Transpose8x8(MutableSpan src, MutableSpan dest) - { - for (int i = 0; i < 8; i++) - { - int i8 = i * 8; - for (int j = 0; j < 8; j++) - { - dest[j * 8 + i] = src[i8 + j]; - } - } - } - - /// - /// The "original" libjpeg/golang based DCT implementation is used as reference implementation for tests. - /// - public static class IntegerReferenceDCT - { - private const int fix_0_298631336 = 2446; - private const int fix_0_390180644 = 3196; - private const int fix_0_541196100 = 4433; - private const int fix_0_765366865 = 6270; - private const int fix_0_899976223 = 7373; - private const int fix_1_175875602 = 9633; - private const int fix_1_501321110 = 12299; - private const int fix_1_847759065 = 15137; - private const int fix_1_961570560 = 16069; - private const int fix_2_053119869 = 16819; - private const int fix_2_562915447 = 20995; - private const int fix_3_072711026 = 25172; - - /// - /// The number of bits - /// - private const int Bits = 13; - - /// - /// The number of bits to shift by on the first pass. - /// - private const int Pass1Bits = 2; - - /// - /// The value to shift by - /// - private const int CenterJSample = 128; - - /// - /// Performs a forward DCT on an 8x8 block of coefficients, including a level shift. - /// Leave results scaled up by an overall factor of 8. - /// - /// The block of coefficients. - public static void TransformFDCTInplace(MutableSpan block) - { - // Pass 1: process rows. - for (int y = 0; y < 8; y++) - { - int y8 = y * 8; - - int x0 = block[y8]; - int x1 = block[y8 + 1]; - int x2 = block[y8 + 2]; - int x3 = block[y8 + 3]; - int x4 = block[y8 + 4]; - int x5 = block[y8 + 5]; - int x6 = block[y8 + 6]; - int x7 = block[y8 + 7]; - - int tmp0 = x0 + x7; - int tmp1 = x1 + x6; - int tmp2 = x2 + x5; - int tmp3 = x3 + x4; - - int tmp10 = tmp0 + tmp3; - int tmp12 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp13 = tmp1 - tmp2; - - tmp0 = x0 - x7; - tmp1 = x1 - x6; - tmp2 = x2 - x5; - tmp3 = x3 - x4; - - block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits; - block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits; - int z1 = (tmp12 + tmp13) * fix_0_541196100; - z1 += 1 << (Bits - Pass1Bits - 1); - block[y8 + 2] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits - Pass1Bits); - block[y8 + 6] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits - Pass1Bits); - - tmp10 = tmp0 + tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp0 + tmp2; - tmp13 = tmp1 + tmp3; - z1 = (tmp12 + tmp13) * fix_1_175875602; - z1 += 1 << (Bits - Pass1Bits - 1); - tmp0 = tmp0 * fix_1_501321110; - tmp1 = tmp1 * fix_3_072711026; - tmp2 = tmp2 * fix_2_053119869; - tmp3 = tmp3 * fix_0_298631336; - tmp10 = tmp10 * -fix_0_899976223; - tmp11 = tmp11 * -fix_2_562915447; - tmp12 = tmp12 * -fix_0_390180644; - tmp13 = tmp13 * -fix_1_961570560; - - tmp12 += z1; - tmp13 += z1; - block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits); - block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits); - block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits); - block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits); - } - - // Pass 2: process columns. - // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8. - for (int x = 0; x < 8; x++) - { - int tmp0 = block[x] + block[56 + x]; - int tmp1 = block[8 + x] + block[48 + x]; - int tmp2 = block[16 + x] + block[40 + x]; - int tmp3 = block[24 + x] + block[32 + x]; - - int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1)); - int tmp12 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp13 = tmp1 - tmp2; - - tmp0 = block[x] - block[56 + x]; - tmp1 = block[8 + x] - block[48 + x]; - tmp2 = block[16 + x] - block[40 + x]; - tmp3 = block[24 + x] - block[32 + x]; - - block[x] = (tmp10 + tmp11) >> Pass1Bits; - block[32 + x] = (tmp10 - tmp11) >> Pass1Bits; - - int z1 = (tmp12 + tmp13) * fix_0_541196100; - z1 += 1 << (Bits + Pass1Bits - 1); - block[16 + x] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits + Pass1Bits); - block[48 + x] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits + Pass1Bits); - - tmp10 = tmp0 + tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp0 + tmp2; - tmp13 = tmp1 + tmp3; - z1 = (tmp12 + tmp13) * fix_1_175875602; - z1 += 1 << (Bits + Pass1Bits - 1); - tmp0 = tmp0 * fix_1_501321110; - tmp1 = tmp1 * fix_3_072711026; - tmp2 = tmp2 * fix_2_053119869; - tmp3 = tmp3 * fix_0_298631336; - tmp10 = tmp10 * -fix_0_899976223; - tmp11 = tmp11 * -fix_2_562915447; - tmp12 = tmp12 * -fix_0_390180644; - tmp13 = tmp13 * -fix_1_961570560; - - tmp12 += z1; - tmp13 += z1; - block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits); - block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits); - block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits); - block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits); - } - - } - private const int w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16) - private const int w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16) - private const int w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16) - private const int w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16) - private const int w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16) - private const int w7 = 565; // 2048*sqrt(2)*cos(7*pi/16) - - private const int w1pw7 = w1 + w7; - private const int w1mw7 = w1 - w7; - private const int w2pw6 = w2 + w6; - private const int w2mw6 = w2 - w6; - private const int w3pw5 = w3 + w5; - private const int w3mw5 = w3 - w5; - - private const int r2 = 181; // 256/sqrt(2) - - /// - /// Performs a 2-D Inverse Discrete Cosine Transformation. - /// - /// The input coefficients should already have been multiplied by the - /// appropriate quantization table. We use fixed-point computation, with the - /// number of bits for the fractional component varying over the intermediate - /// stages. - /// - /// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the - /// discrete W transform and for the discrete Fourier transform", IEEE Trans. on - /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. - /// - /// The source block of coefficients - public static void TransformIDCTInplace(MutableSpan src) - { - // Horizontal 1-D IDCT. - for (int y = 0; y < 8; y++) - { - int y8 = y * 8; - - // If all the AC components are zero, then the IDCT is trivial. - if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 && - src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0) - { - int dc = src[y8 + 0] << 3; - src[y8 + 0] = dc; - src[y8 + 1] = dc; - src[y8 + 2] = dc; - src[y8 + 3] = dc; - src[y8 + 4] = dc; - src[y8 + 5] = dc; - src[y8 + 6] = dc; - src[y8 + 7] = dc; - continue; - } - - // Prescale. - int x0 = (src[y8 + 0] << 11) + 128; - int x1 = src[y8 + 4] << 11; - int x2 = src[y8 + 6]; - int x3 = src[y8 + 2]; - int x4 = src[y8 + 1]; - int x5 = src[y8 + 7]; - int x6 = src[y8 + 5]; - int x7 = src[y8 + 3]; - - // Stage 1. - int x8 = w7 * (x4 + x5); - x4 = x8 + (w1mw7 * x4); - x5 = x8 - (w1pw7 * x5); - x8 = w3 * (x6 + x7); - x6 = x8 - (w3mw5 * x6); - x7 = x8 - (w3pw5 * x7); - - // Stage 2. - x8 = x0 + x1; - x0 -= x1; - x1 = w6 * (x3 + x2); - x2 = x1 - (w2pw6 * x2); - x3 = x1 + (w2mw6 * x3); - x1 = x4 + x6; - x4 -= x6; - x6 = x5 + x7; - x5 -= x7; - - // Stage 3. - x7 = x8 + x3; - x8 -= x3; - x3 = x0 + x2; - x0 -= x2; - x2 = ((r2 * (x4 + x5)) + 128) >> 8; - x4 = ((r2 * (x4 - x5)) + 128) >> 8; - - // Stage 4. - src[y8 + 0] = (x7 + x1) >> 8; - src[y8 + 1] = (x3 + x2) >> 8; - src[y8 + 2] = (x0 + x4) >> 8; - src[y8 + 3] = (x8 + x6) >> 8; - src[y8 + 4] = (x8 - x6) >> 8; - src[y8 + 5] = (x0 - x4) >> 8; - src[y8 + 6] = (x3 - x2) >> 8; - src[y8 + 7] = (x7 - x1) >> 8; - } - - // Vertical 1-D IDCT. - for (int x = 0; x < 8; x++) - { - // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial. - // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so - // we do not bother to check for the all-zero case. - - // Prescale. - int y0 = (src[x] << 8) + 8192; - int y1 = src[32 + x] << 8; - int y2 = src[48 + x]; - int y3 = src[16 + x]; - int y4 = src[8 + x]; - int y5 = src[56 + x]; - int y6 = src[40 + x]; - int y7 = src[24 + x]; - - // Stage 1. - int y8 = (w7 * (y4 + y5)) + 4; - y4 = (y8 + (w1mw7 * y4)) >> 3; - y5 = (y8 - (w1pw7 * y5)) >> 3; - y8 = (w3 * (y6 + y7)) + 4; - y6 = (y8 - (w3mw5 * y6)) >> 3; - y7 = (y8 - (w3pw5 * y7)) >> 3; - - // Stage 2. - y8 = y0 + y1; - y0 -= y1; - y1 = (w6 * (y3 + y2)) + 4; - y2 = (y1 - (w2pw6 * y2)) >> 3; - y3 = (y1 + (w2mw6 * y3)) >> 3; - y1 = y4 + y6; - y4 -= y6; - y6 = y5 + y7; - y5 -= y7; - - // Stage 3. - y7 = y8 + y3; - y8 -= y3; - y3 = y0 + y2; - y0 -= y2; - y2 = ((r2 * (y4 + y5)) + 128) >> 8; - y4 = ((r2 * (y4 - y5)) + 128) >> 8; - - // Stage 4. - src[x] = (y7 + y1) >> 14; - src[8 + x] = (y3 + y2) >> 14; - src[16 + x] = (y0 + y4) >> 14; - src[24 + x] = (y8 + y6) >> 14; - src[32 + x] = (y8 - y6) >> 14; - src[40 + x] = (y0 - y4) >> 14; - src[48 + x] = (y3 - y2) >> 14; - src[56 + x] = (y7 - y1) >> 14; - } - } - } - - /// - /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200 - /// - /// - /// - private static void iDCT1Dllm_32f(MutableSpan y, MutableSpan x) - { - float a0, a1, a2, a3, b0, b1, b2, b3; - float z0, z1, z2, z3, z4; - - //float r0 = 1.414214f; - float r1 = 1.387040f; - float r2 = 1.306563f; - float r3 = 1.175876f; - //float r4 = 1.000000f; - float r5 = 0.785695f; - float r6 = 0.541196f; - float r7 = 0.275899f; - - z0 = y[1] + y[7]; - z1 = y[3] + y[5]; - z2 = y[3] + y[7]; - z3 = y[1] + y[5]; - z4 = (z0 + z1) * r3; - - z0 = z0 * (-r3 + r7); - z1 = z1 * (-r3 - r1); - z2 = z2 * (-r3 - r5) + z4; - z3 = z3 * (-r3 + r5) + z4; - - b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2; - b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3; - b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2; - b0 = y[1] * (r1 + r3 - r5 - r7) + z0 + z3; - - z4 = (y[2] + y[6]) * r6; - z0 = y[0] + y[4]; - z1 = y[0] - y[4]; - z2 = z4 - y[6] * (r2 + r6); - z3 = z4 + y[2] * (r2 - r6); - a0 = z0 + z3; - a3 = z0 - z3; - a1 = z1 + z2; - a2 = z1 - z2; - - x[0] = a0 + b0; - x[7] = a0 - b0; - x[1] = a1 + b1; - x[6] = a1 - b1; - x[2] = a2 + b2; - x[5] = a2 - b2; - x[3] = a3 + b3; - x[4] = a3 - b3; - } - - /// - /// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 - /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp" - /// - /// - /// - /// - internal static void iDCT2D_llm(MutableSpan s, MutableSpan d, MutableSpan temp) - { - int j; - - for (j = 0; j < 8; j++) - { - iDCT1Dllm_32f(s.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - for (j = 0; j < 8; j++) - { - iDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - for (j = 0; j < 64; j++) - { - d[j] *= 0.125f; - } - } - - /// - /// Original: - /// - /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15 - /// - /// - /// Source - /// Destination - public static void fDCT2D8x4_32f(MutableSpan s, MutableSpan d) - { - Vector4 c0 = _mm_load_ps(s, 0); - Vector4 c1 = _mm_load_ps(s, 56); - Vector4 t0 = (c0 + c1); - Vector4 t7 = (c0 - c1); - - c1 = _mm_load_ps(s, 48); - c0 = _mm_load_ps(s, 8); - Vector4 t1 = (c0 + c1); - Vector4 t6 = (c0 - c1); - - c1 = _mm_load_ps(s, 40); - c0 = _mm_load_ps(s, 16); - Vector4 t2 = (c0 + c1); - Vector4 t5 = (c0 - c1); - - c0 = _mm_load_ps(s, 24); - c1 = _mm_load_ps(s, 32); - Vector4 t3 = (c0 + c1); - Vector4 t4 = (c0 - c1); - - /* - c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; - c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; - c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; - c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; - */ - - c0 = (t0 + t3); - Vector4 c3 = (t0 - t3); - c1 = (t1 + t2); - Vector4 c2 = (t1 - t2); - - /* - c0 = t0 + t3; c3 = t0 - t3; - c1 = t1 + t2; c2 = t1 - t2; - */ - - _mm_store_ps(d, 0, (c0 + c1)); - - _mm_store_ps(d, 32, (c0 - c1)); - - /*y[0] = c0 + c1; - y[4] = c0 - c1;*/ - - Vector4 w0 = new Vector4(0.541196f); - Vector4 w1 = new Vector4(1.306563f); - - _mm_store_ps(d, 16, ((w0 * c2) + (w1 * c3))); - - _mm_store_ps(d, 48, ((w0 * c3) - (w1 * c2))); - /* - y[2] = c2 * r[6] + c3 * r[2]; - y[6] = c3 * r[6] - c2 * r[2]; - */ - - w0 = new Vector4(1.175876f); - w1 = new Vector4(0.785695f); - c3 = ((w0 * t4) + (w1 * t7)); - c0 = ((w0 * t7) - (w1 * t4)); - /* - c3 = t4 * r[3] + t7 * r[5]; - c0 = t7 * r[3] - t4 * r[5]; - */ - - w0 = new Vector4(1.387040f); - w1 = new Vector4(0.275899f); - c2 = ((w0 * t5) + (w1 * t6)); - c1 = ((w0 * t6) - (w1 * t5)); - /* - c2 = t5 * r[1] + t6 * r[7]; - c1 = t6 * r[1] - t5 * r[7]; - */ - - _mm_store_ps(d, 24, (c0 - c2)); - - _mm_store_ps(d, 40, (c3 - c1)); - //y[5] = c3 - c1; y[3] = c0 - c2; - - Vector4 invsqrt2 = new Vector4(0.707107f); - c0 = ((c0 + c2) * invsqrt2); - c3 = ((c3 + c1) * invsqrt2); - //c0 = (c0 + c2) * invsqrt2; - //c3 = (c3 + c1) * invsqrt2; - - _mm_store_ps(d, 8, (c0 + c3)); - - _mm_store_ps(d, 56, (c0 - c3)); - //y[1] = c0 + c3; y[7] = c0 - c3; - - /*for(i = 0;i < 8;i++) - { - y[i] *= invsqrt2h; - }*/ - } - - public static void fDCT8x8_llm_sse(MutableSpan s, MutableSpan d, MutableSpan temp) - { - Transpose8x8(s, temp); - - fDCT2D8x4_32f(temp, d); - - fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - - Transpose8x8(d, temp); - - fDCT2D8x4_32f(temp, d); - - fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); - - Vector4 c = new Vector4(0.1250f); - - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//0 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//1 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//2 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//3 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//4 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//5 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//6 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//7 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//8 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//9 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//10 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//11 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//12 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//13 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//14 - _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//15 - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector4 _mm_load_ps(MutableSpan src, int offset) - { - src = src.Slice(offset); - return new Vector4(src[0], src[1], src[2], src[3]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void _mm_store_ps(MutableSpan dest, int offset, Vector4 src) - { - dest = dest.Slice(offset); - dest[0] = src.X; - dest[1] = src.Y; - dest[2] = src.Z; - dest[3] = src.W; - } - - private static readonly Vector4 _1_175876 = new Vector4(1.175876f); - - private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); - - private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); - - private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); - - private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); - - private static readonly Vector4 _0_298631 = new Vector4(0.298631f); - - private static readonly Vector4 _2_053120 = new Vector4(2.053120f); - - private static readonly Vector4 _3_072711 = new Vector4(3.072711f); - - private static readonly Vector4 _1_501321 = new Vector4(1.501321f); - - private static readonly Vector4 _0_541196 = new Vector4(0.541196f); - - private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); - - private static readonly Vector4 _0_765367 = new Vector4(0.765367f); - - /// - /// Original: - /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 - /// Does a part of the IDCT job on the given parts of the blocks - /// - /// - /// - internal static void iDCT2D8x4_32f(MutableSpan y, MutableSpan x) - { - /* - float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; - for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } - */ - /* - 0: 1.414214 - 1: 1.387040 - 2: 1.306563 - 3: - 4: 1.000000 - 5: 0.785695 - 6: - 7: 0.275899 - */ - - Vector4 my1 = _mm_load_ps(y, 8); - Vector4 my7 = _mm_load_ps(y, 56); - Vector4 mz0 = my1 + my7; - - Vector4 my3 = _mm_load_ps(y, 24); - Vector4 mz2 = my3 + my7; - Vector4 my5 = _mm_load_ps(y, 40); - Vector4 mz1 = my3 + my5; - Vector4 mz3 = my1 + my5; - - Vector4 mz4 = ((mz0 + mz1) * _1_175876); - //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; - //z4 = (z0 + z1) * r[3]; - - mz2 = mz2 * _1_961571 + mz4; - mz3 = mz3 * _0_390181 + mz4; - mz0 = mz0 * _0_899976; - mz1 = mz1 * _2_562915; - - /* - -0.899976 - -2.562915 - -1.961571 - -0.390181 - z0 = z0 * (-r[3] + r[7]); - z1 = z1 * (-r[3] - r[1]); - z2 = z2 * (-r[3] - r[5]) + z4; - z3 = z3 * (-r[3] + r[5]) + z4;*/ - - Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; - Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; - Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; - Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; - - /* - 0.298631 - 2.053120 - 3.072711 - 1.501321 - b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; - b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; - b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; - b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; - */ - - Vector4 my2 = _mm_load_ps(y, 16); - Vector4 my6 = _mm_load_ps(y, 48); - mz4 = (my2 + my6) * _0_541196; - Vector4 my0 = _mm_load_ps(y, 0); - Vector4 my4 = _mm_load_ps(y, 32); - mz0 = my0 + my4; - mz1 = my0 - my4; - - mz2 = mz4 + my6 * _1_847759; - mz3 = mz4 + my2 * _0_765367; - - my0 = mz0 + mz3; - my3 = mz0 - mz3; - my1 = mz1 + mz2; - my2 = mz1 - mz2; - /* - 1.847759 - 0.765367 - z4 = (y[2] + y[6]) * r[6]; - z0 = y[0] + y[4]; z1 = y[0] - y[4]; - z2 = z4 - y[6] * (r[2] + r[6]); - z3 = z4 + y[2] * (r[2] - r[6]); - a0 = z0 + z3; a3 = z0 - z3; - a1 = z1 + z2; a2 = z1 - z2; - */ - - _mm_store_ps(x, 0, my0 + mb0); - - _mm_store_ps(x, 56, my0 - mb0); - - _mm_store_ps(x, 8, my1 + mb1); - - _mm_store_ps(x, 48, my1 - mb1); - - _mm_store_ps(x, 16, my2 + mb2); - - _mm_store_ps(x, 40, my2 - mb2); - - _mm_store_ps(x, 24, my3 + mb3); - - _mm_store_ps(x, 32, my3 - mb3); - /* - x[0] = a0 + b0; x[7] = a0 - b0; - x[1] = a1 + b1; x[6] = a1 - b1; - x[2] = a2 + b2; x[5] = a2 - b2; - x[3] = a3 + b3; x[4] = a3 - b3; - for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } - */ - } - - /// - /// Copies color values from block to the destination image buffer. - /// - /// - /// - /// - internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan buffer, int stride) - { - fixed (Block8x8F* p = &block) - { - float* b = (float*)p; - - for (int y = 0; y < 8; y++) - { - int y8 = y * 8; - int yStride = y * stride; - - for (int x = 0; x < 8; x++) - { - float c = b[y8 + x]; - - if (c < -128) - { - c = 0; - } - else if (c > 127) - { - c = 255; - } - else - { - c += 128; - } - - buffer[yStride + x] = (byte)c; - } - } - } - } - - internal static void fDCT1Dllm_32f(MutableSpan x, MutableSpan y) - { - float t0, t1, t2, t3, t4, t5, t6, t7; - float c0, c1, c2, c3; - float[] r = new float[8]; - - //for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } - r[0] = 1.414214f; - r[1] = 1.387040f; - r[2] = 1.306563f; - r[3] = 1.175876f; - r[4] = 1.000000f; - r[5] = 0.785695f; - r[6] = 0.541196f; - r[7] = 0.275899f; - - const float invsqrt2 = 0.707107f; //(float)(1.0f / M_SQRT2); - //const float invsqrt2h = 0.353554f; //invsqrt2*0.5f; - - c1 = x[0]; - c2 = x[7]; - t0 = c1 + c2; - t7 = c1 - c2; - c1 = x[1]; - c2 = x[6]; - t1 = c1 + c2; - t6 = c1 - c2; - c1 = x[2]; - c2 = x[5]; - t2 = c1 + c2; - t5 = c1 - c2; - c1 = x[3]; - c2 = x[4]; - t3 = c1 + c2; - t4 = c1 - c2; - - c0 = t0 + t3; - c3 = t0 - t3; - c1 = t1 + t2; - c2 = t1 - t2; - - y[0] = c0 + c1; - y[4] = c0 - c1; - y[2] = c2 * r[6] + c3 * r[2]; - y[6] = c3 * r[6] - c2 * r[2]; - - c3 = t4 * r[3] + t7 * r[5]; - c0 = t7 * r[3] - t4 * r[5]; - c2 = t5 * r[1] + t6 * r[7]; - c1 = t6 * r[1] - t5 * r[7]; - - y[5] = c3 - c1; - y[3] = c0 - c2; - c0 = (c0 + c2) * invsqrt2; - c3 = (c3 + c1) * invsqrt2; - y[1] = c0 + c3; - y[7] = c0 - c3; - } - - internal static void fDCT2D_llm( - MutableSpan s, - MutableSpan d, - MutableSpan temp, - bool downscaleBy8 = false, - bool offsetSourceByNeg128 = false) - { - MutableSpan sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s; - - for (int j = 0; j < 8; j++) - { - fDCT1Dllm_32f(sWorker.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - for (int j = 0; j < 8; j++) - { - fDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); - } - - Transpose8x8(temp, d); - - if (downscaleBy8) - { - for (int j = 0; j < 64; j++) - { - d[j] *= 0.125f; - } - } - } - - /// - /// Reference implementation to test . - /// Rounding is done used an integer-based algorithm defined in . - /// - /// The input block - /// The destination block of integers - /// The quantization table - /// Pointer to - public static unsafe void UnZigDivRoundRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr) - { - float* s = (float*)src; - float* q = (float*)qt; - - for (int zig = 0; zig < Block8x8F.ScalarCount; zig++) - { - int a = (int)s[unzigPtr[zig]]; - int b = (int)q[zig]; - - int val = RationalRound(a, b); - dest[zig] = val; - } - } - - /// - /// Rounds a rational number defined as dividend/divisor into an integer - /// - /// The dividend - /// The divisior - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int RationalRound(int dividend, int divisor) - { - if (dividend >= 0) - { - return (dividend + (divisor >> 1)) / divisor; - } - - return -((-dividend + (divisor >> 1)) / divisor); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs new file mode 100644 index 000000000..6b9e98d66 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs @@ -0,0 +1,36 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public partial class ReferenceImplementationsTests + { + public class AccurateDCT : JpegFixture + { + public AccurateDCT(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData(42)] + [InlineData(1)] + [InlineData(2)] + public void ForwardThenInverse(int seed) + { + float[] data = JpegFixture.Create8x8RandomFloatData(-1000, 1000, seed); + + var b0 = default(Block8x8F); + b0.LoadFrom(data); + + Block8x8F b1 = ReferenceImplementations.AccurateDCT.TransformFDCT(ref b0); + Block8x8F b2 = ReferenceImplementations.AccurateDCT.TransformIDCT(ref b1); + + this.CompareBlocks(b0, b2, 1e-4f); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs new file mode 100644 index 000000000..1fc47726b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -0,0 +1,123 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public partial class ReferenceImplementationsTests + { + public class FastFloatingPointDCT : JpegFixture + { + public FastFloatingPointDCT(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData(42, 0)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public void LLM_ForwardThenInverse(int seed, int startAt) + { + int[] data = JpegFixture.Create8x8RandomIntData(-1000, 1000, seed); + float[] original = data.ConvertAllToFloat(); + float[] src = data.ConvertAllToFloat(); + float[] dest = new float[64]; + float[] temp = new float[64]; + + ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, dest, temp, true); + ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D_llm(dest, src, temp); + + this.CompareBlocks(original, src, 0.1f); + } + + // [Fact] + public void LLM_CalcConstants() + { + ReferenceImplementations.LLM_FloatingPoint_DCT.PrintConstants(this.Output); + } + + [Theory] + [InlineData(42, 1000)] + [InlineData(1, 1000)] + [InlineData(2, 1000)] + [InlineData(42, 200)] + [InlineData(1, 200)] + [InlineData(2, 200)] + public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) + { + float[] sourceArray = JpegFixture.Create8x8RoundedRandomFloatData(-range, range, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + + Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); + + this.CompareBlocks(expected, actual, 0.1f); + } + + [Theory] + [InlineData(42, 1000)] + [InlineData(1, 1000)] + [InlineData(2, 1000)] + public void LLM_IDCT_CompareToIntegerRoundedAccurateImplementation(int seed, int range) + { + Block8x8F fSource = CreateRoundedRandomFloatBlock(-range, range, seed); + Block8x8 iSource = fSource.RoundAsInt16Block(); + + Block8x8 iExpected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref iSource); + Block8x8F fExpected = iExpected.AsFloatBlock(); + + Block8x8F fActual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref fSource); + + this.CompareBlocks(fExpected, fActual, 2); + } + + + [Theory] + [InlineData(42)] + [InlineData(1)] + [InlineData(2)] + public void LLM_FDCT_IsEquivalentTo_AccurateImplementation(int seed) + { + float[] floatData = JpegFixture.Create8x8RandomFloatData(-1000, 1000); + + Block8x8F source = default(Block8x8F); + source.LoadFrom(floatData); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); + Block8x8F actual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformFDCT_UpscaleBy8(ref source); + actual /= 8; + + this.CompareBlocks(expected, actual, 1f); + } + + [Theory] + [InlineData(42, 1000)] + [InlineData(1, 1000)] + [InlineData(2, 1000)] + [InlineData(42, 200)] + [InlineData(1, 200)] + [InlineData(2, 200)] + public void GT_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) + { + int[] intData = JpegFixture.Create8x8RandomIntData(-range, range, seed); + float[] floatSrc = intData.ConvertAllToFloat(); + + ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData); + + float[] dest = new float[64]; + + ReferenceImplementations.GT_FloatingPoint_DCT.iDCT8x8GT(floatSrc, dest); + + this.CompareBlocks(intData.ConvertAllToFloat(), dest, 1f); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs new file mode 100644 index 000000000..f384a76c4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs @@ -0,0 +1,90 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public partial class ReferenceImplementationsTests + { + public class StandardIntegerDCT : JpegFixture + { + public StandardIntegerDCT(ITestOutputHelper output) + : base(output) + { + } + + [Theory] + [InlineData(42, 200)] + [InlineData(1, 200)] + [InlineData(2, 200)] + public void IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range) + { + int[] data = Create8x8RandomIntData(-range, range, seed); + + Block8x8 source = default(Block8x8); + source.LoadFrom(data); + + Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + Block8x8 actual = ReferenceImplementations.StandardIntegerDCT.TransformIDCT(ref source); + + this.CompareBlocks(expected, actual, 1); + } + + [Theory] + [InlineData(42)] + [InlineData(1)] + [InlineData(2)] + public void FDCT_IsEquivalentTo_AccurateImplementation(int seed) + { + int[] data = Create8x8RandomIntData(-1000, 1000, seed); + + Block8x8F source = default(Block8x8F); + source.LoadFrom(data); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); + + source += 128; + Block8x8 temp = source.RoundAsInt16Block(); + Block8x8 actual8 = ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8(ref temp); + Block8x8F actual = actual8.AsFloatBlock(); + actual /= 8; + + this.CompareBlocks(expected, actual, 1f); + } + + + [Theory] + [InlineData(42, 0)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public void ForwardThenInverse(int seed, int startAt) + { + Span original = JpegFixture.Create8x8RandomIntData(-200, 200, seed); + + Span block = original.AddScalarToAllValues(128); + + ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8_Inplace(block); + + for (int i = 0; i < 64; i++) + { + block[i] /= 8; + } + + ReferenceImplementations.StandardIntegerDCT.TransformIDCTInplace(block); + + for (int i = startAt; i < 64; i++) + { + float expected = original[i]; + float actual = (float)block[i]; + + Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs index 50b94bc24..26ec454f9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs @@ -1,124 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; + +using Xunit.Abstractions; // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Formats.Jpg +namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - using ImageSharp.Formats.Jpg; - - using Xunit; - using Xunit.Abstractions; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; - public class ReferenceImplementationsTests : JpegUtilityTestFixture + public partial class ReferenceImplementationsTests : JpegFixture { public ReferenceImplementationsTests(ITestOutputHelper output) : base(output) { } - - - [Theory] - [InlineData(42)] - [InlineData(1)] - [InlineData(2)] - public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) - { - MutableSpan intData = Create8x8RandomIntData(-200, 200, seed); - MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan(); - - ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData); - - MutableSpan dest = new MutableSpan(64); - MutableSpan temp = new MutableSpan(64); - - ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp); - - for (int i = 0; i < 64; i++) - { - float expected = intData[i]; - float actual = dest[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); - } - } - - [Theory] - [InlineData(42, 0)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public void IntegerDCT_ForwardThenInverse(int seed, int startAt) - { - MutableSpan original = Create8x8RandomIntData(-200, 200, seed); - - MutableSpan block = original.AddScalarToAllValues(128); - - ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block); - - for (int i = 0; i < 64; i++) - { - block[i] /= 8; - } - - ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block); - - for (int i = startAt; i < 64; i++) - { - float expected = original[i]; - float actual = (float)block[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); - } - - } - - [Theory] - [InlineData(42, 0)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt) - { - int[] data = Create8x8RandomIntData(-200, 200, seed); - MutableSpan src = new MutableSpan(data).ConvertToFloat32MutableSpan(); - MutableSpan dest = new MutableSpan(64); - MutableSpan temp = new MutableSpan(64); - - ReferenceImplementations.fDCT2D_llm(src, dest, temp, true); - ReferenceImplementations.iDCT2D_llm(dest, src, temp); - - for (int i = startAt; i < 64; i++) - { - float expected = data[i]; - float actual = (float)src[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); - } - } - - [Theory] - [InlineData(42)] - [InlineData(1)] - [InlineData(2)] - public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) - { - MutableSpan intData = Create8x8RandomIntData(-200, 200, seed); - MutableSpan floatSrc = intData.ConvertToFloat32MutableSpan(); - - ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData); - - MutableSpan dest = new MutableSpan(64); - MutableSpan temp = new MutableSpan(64); - - ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); - - for (int i = 0; i < 64; i++) - { - float expected = intData[i]; - float actual = dest[i]; - - Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs new file mode 100644 index 000000000..6e68c43f2 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs @@ -0,0 +1,168 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System; + using System.IO; + using System.Linq; + + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; + + using Xunit; + using Xunit.Abstractions; + + public class SpectralJpegTests + { + public SpectralJpegTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + public static readonly string[] BaselineTestJpegs = + { + TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Jpeg400, + TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Testorig420, + TestImages.Jpeg.Baseline.Jpeg420Small, TestImages.Jpeg.Baseline.Bad.BadEOF, + TestImages.Jpeg.Baseline.Bad.ExifUndefType, + }; + + public static readonly string[] ProgressiveTestJpegs = + { + TestImages.Jpeg.Progressive.Fb, TestImages.Jpeg.Progressive.Progress, + TestImages.Jpeg.Progressive.Festzug, TestImages.Jpeg.Progressive.Bad.BadEOF + }; + + public static readonly string[] AllTestJpegs = BaselineTestJpegs.Concat(ProgressiveTestJpegs).ToArray(); + + [Theory] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void PdfJsDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + where TPixel : struct, IPixel + { + PdfJsJpegDecoderCore decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms); + + var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + VerifyJpeg.SaveSpectralImage(provider, data); + } + } + + [Theory] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void OriginalDecoder_ParseStream_SaveSpectralResult(TestImageProvider provider) + where TPixel : struct, IPixel + { + OrigJpegDecoderCore decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms, false); + + var data = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + VerifyJpeg.SaveSpectralImage(provider, data); + } + } + + private void VerifySpectralCorrectness( + TestImageProvider provider, + LibJpegTools.SpectralData imageSharpData) + where TPixel : struct, IPixel + { + var libJpegData = LibJpegTools.ExtractSpectralData(provider.SourceFileOrDescription); + + bool equality = libJpegData.Equals(imageSharpData); + this.Output.WriteLine("Spectral data equality: " + equality); + + int componentCount = imageSharpData.ComponentCount; + if (libJpegData.ComponentCount != componentCount) + { + throw new Exception("libJpegData.ComponentCount != componentCount"); + } + + double averageDifference = 0; + double totalDifference = 0; + double tolerance = 0; + + this.Output.WriteLine("*** Differences ***"); + for (int i = 0; i < componentCount; i++) + { + LibJpegTools.ComponentData libJpegComponent = libJpegData.Components[i]; + LibJpegTools.ComponentData imageSharpComponent = imageSharpData.Components[i]; + + (double total, double average) diff = LibJpegTools.CalculateDifference(libJpegComponent, imageSharpComponent); + + this.Output.WriteLine($"Component{i}: {diff}"); + averageDifference += diff.average; + totalDifference += diff.total; + tolerance += libJpegComponent.SpectralBlocks.Length; + } + averageDifference /= componentCount; + + tolerance /= 64; // fair enough? + + this.Output.WriteLine($"AVERAGE: {averageDifference}"); + this.Output.WriteLine($"TOTAL: {totalDifference}"); + this.Output.WriteLine($"TOLERANCE = totalNumOfBlocks / 64 = {tolerance}"); + + Assert.True(totalDifference < tolerance); + } + + [Theory(Skip = "Debug/Comparison only")] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void VerifySpectralCorrectness_PdfJs(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.IsWindows) + { + return; + } + + PdfJsJpegDecoderCore decoder = new PdfJsJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms); + var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + + this.VerifySpectralCorrectness(provider, imageSharpData); + } + } + + [Theory] + [WithFileCollection(nameof(AllTestJpegs), PixelTypes.Rgba32)] + public void VerifySpectralResults_OriginalDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (!TestEnvironment.IsWindows) + { + return; + } + + OrigJpegDecoderCore decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder()); + + byte[] sourceBytes = TestFile.Create(provider.SourceFileOrDescription).Bytes; + + using (var ms = new MemoryStream(sourceBytes)) + { + decoder.ParseStream(ms); + var imageSharpData = LibJpegTools.SpectralData.LoadFromImageSharpDecoder(decoder); + + this.VerifySpectralCorrectness(provider, imageSharpData); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs new file mode 100644 index 000000000..07268ef21 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -0,0 +1,190 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + + +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Text; + + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + + using Xunit; + using Xunit.Abstractions; + + public class JpegFixture : MeasureFixture + { + public JpegFixture(ITestOutputHelper output) : base(output) + { + } + + // ReSharper disable once InconsistentNaming + public static float[] Create8x8FloatData() + { + float[] result = new float[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = i * 10 + j; + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static int[] Create8x8IntData() + { + int[] result = new int[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = i * 10 + j; + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static short[] Create8x8ShortData() + { + short[] result = new short[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = (short)(i * 10 + j); + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static int[] Create8x8RandomIntData(int minValue, int maxValue, int seed = 42) + { + Random rnd = new Random(seed); + int[] result = new int[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = rnd.Next(minValue, maxValue); + } + } + return result; + } + + internal static float[] Create8x8RoundedRandomFloatData(int minValue, int maxValue, int seed = 42) + => Create8x8RandomIntData(minValue, maxValue, seed).ConvertAllToFloat(); + + public static float[] Create8x8RandomFloatData(float minValue, float maxValue, int seed = 42) + { + Random rnd = new Random(seed); + float[] result = new float[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + double val = rnd.NextDouble(); + val *= maxValue - minValue; + val += minValue; + + result[i * 8 + j] = (float)val; + } + } + return result; + } + + internal static Block8x8F CreateRandomFloatBlock(float minValue, float maxValue, int seed = 42) => + Block8x8F.Load(Create8x8RandomFloatData(minValue, maxValue, seed)); + + internal static Block8x8F CreateRoundedRandomFloatBlock(int minValue, int maxValue, int seed = 42) => + Block8x8F.Load(Create8x8RoundedRandomFloatData(minValue, maxValue, seed)); + + internal void Print8x8Data(T[] data) => this.Print8x8Data(new Span(data)); + + internal void Print8x8Data(Span data) + { + StringBuilder bld = new StringBuilder(); + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + bld.Append($"{data[i * 8 + j],3} "); + } + bld.AppendLine(); + } + + this.Output.WriteLine(bld.ToString()); + } + + internal void PrintLinearData(T[] data) => this.PrintLinearData(new Span(data), data.Length); + + internal void PrintLinearData(Span data, int count = -1) + { + if (count < 0) count = data.Length; + + StringBuilder bld = new StringBuilder(); + for (int i = 0; i < count; i++) + { + bld.Append($"{data[i],3} "); + } + this.Output.WriteLine(bld.ToString()); + } + + protected void Print(string msg) + { + Debug.WriteLine(msg); + this.Output.WriteLine(msg); + } + + internal void CompareBlocks(Block8x8 a, Block8x8 b, int tolerance) => + this.CompareBlocks(a.AsFloatBlock(), b.AsFloatBlock(), (float)tolerance + 1e-5f); + + internal void CompareBlocks(Block8x8F a, Block8x8F b, float tolerance) + => this.CompareBlocks(a.ToArray(), b.ToArray(), tolerance); + + internal void CompareBlocks(Span a, Span b, float tolerance) + { + ApproximateFloatComparer comparer = new ApproximateFloatComparer(tolerance); + double totalDifference = 0.0; + + bool failed = false; + + for (int i = 0; i < 64; i++) + { + float expected = a[i]; + float actual = b[i]; + totalDifference += Math.Abs(expected - actual); + + if (!comparer.Equals(expected, actual)) + { + failed = true; + this.Output.WriteLine($"Difference too large at index {i}"); + } + } + + this.Output.WriteLine("TOTAL DIFF: "+totalDifference); + Assert.False(failed); + } + + internal static OrigJpegDecoderCore ParseStream(string testFileName, bool metaDataOnly = false) + { + byte[] bytes = TestFile.Create(testFileName).Bytes; + using (var ms = new MemoryStream(bytes)) + { + var decoder = new OrigJpegDecoderCore(Configuration.Default, new JpegDecoder()); + decoder.ParseStream(ms, metaDataOnly); + return decoder; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs new file mode 100644 index 000000000..40b41b9cb --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -0,0 +1,196 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Linq; + using System.Numerics; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + using SixLabors.ImageSharp.Memory; + using SixLabors.Primitives; + + internal static partial class LibJpegTools + { + /// + /// Stores spectral blocks for jpeg components. + /// + public class ComponentData : IEquatable, IJpegComponent + { + public ComponentData(int widthInBlocks, int heightInBlocks, int index) + { + this.HeightInBlocks = heightInBlocks; + this.WidthInBlocks = widthInBlocks; + this.Index = index; + this.SpectralBlocks = new Buffer2D(this.WidthInBlocks, this.HeightInBlocks); + } + + public Size Size => new Size(this.WidthInBlocks, this.HeightInBlocks); + + public int Index { get; } + + public Size SizeInBlocks => new Size(this.WidthInBlocks, this.HeightInBlocks); + + public Size SamplingFactors => throw new NotSupportedException(); + + public Size SubSamplingDivisors => throw new NotSupportedException(); + + public int HeightInBlocks { get; } + + public int WidthInBlocks { get; } + + public int QuantizationTableIndex => throw new NotSupportedException(); + + public Buffer2D SpectralBlocks { get; private set; } + + public short MinVal { get; private set; } = short.MaxValue; + + public short MaxVal { get; private set; } = short.MinValue; + + internal void MakeBlock(short[] data, int y, int x) + { + this.MinVal = Math.Min((short)this.MinVal, data.Min()); + this.MaxVal = Math.Max((short)this.MaxVal, data.Max()); + this.SpectralBlocks[x, y] = new Block8x8(data); + } + + public static ComponentData Load(PdfJsFrameComponent c, int index) + { + var result = new ComponentData( + c.WidthInBlocks, + c.HeightInBlocks, + index + ); + + for (int y = 0; y < result.HeightInBlocks; y++) + { + for (int x = 0; x < result.WidthInBlocks; x++) + { + short[] data = c.GetBlockBuffer(y, x).ToArray(); + result.MakeBlock(data, y, x); + } + } + + return result; + } + + public static ComponentData Load(OrigComponent c) + { + var result = new ComponentData( + c.SizeInBlocks.Width, + c.SizeInBlocks.Height, + c.Index + ); + + for (int y = 0; y < result.HeightInBlocks; y++) + { + for (int x = 0; x < result.WidthInBlocks; x++) + { + short[] data = c.GetBlockReference(x, y).ToArray(); + result.MakeBlock(data, y, x); + } + } + + return result; + } + + public Image CreateGrayScaleImage() + { + Image result = new Image(this.WidthInBlocks * 8, this.HeightInBlocks * 8); + + for (int by = 0; by < this.HeightInBlocks; by++) + { + for (int bx = 0; bx < this.WidthInBlocks; bx++) + { + this.WriteToImage(bx, by, result); + } + } + return result; + } + + internal void WriteToImage(int bx, int by, Image image) + { + Block8x8 block = this.SpectralBlocks[bx, by]; + + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + var val = this.GetBlockValue(block, x, y); + + Vector4 v = new Vector4(val, val, val, 1); + Rgba32 color = default(Rgba32); + color.PackFromVector4(v); + + int yy = by * 8 + y; + int xx = bx * 8 + x; + image[xx, yy] = color; + } + } + } + + internal float GetBlockValue(Block8x8 block, int x, int y) + { + float d = (this.MaxVal - this.MinVal); + float val = block[y, x]; + val -= this.MinVal; + val /= d; + return val; + } + + public bool Equals(ComponentData other) + { + if (Object.ReferenceEquals(null, other)) return false; + if (Object.ReferenceEquals(this, other)) return true; + bool ok = this.Index == other.Index && this.HeightInBlocks == other.HeightInBlocks + && this.WidthInBlocks == other.WidthInBlocks; + //&& this.MinVal == other.MinVal + //&& this.MaxVal == other.MaxVal; + if (!ok) return false; + + for (int y = 0; y < this.HeightInBlocks; y++) + { + for (int x = 0; x < this.WidthInBlocks; x++) + { + Block8x8 a = this.SpectralBlocks[x, y]; + Block8x8 b = other.SpectralBlocks[x, y]; + if (!a.Equals(b)) return false; + } + } + return true; + } + + public override bool Equals(object obj) + { + if (Object.ReferenceEquals(null, obj)) return false; + if (Object.ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return this.Equals((ComponentData)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = this.Index; + hashCode = (hashCode * 397) ^ this.HeightInBlocks; + hashCode = (hashCode * 397) ^ this.WidthInBlocks; + hashCode = (hashCode * 397) ^ this.MinVal.GetHashCode(); + hashCode = (hashCode * 397) ^ this.MaxVal.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(ComponentData left, ComponentData right) + { + return Object.Equals(left, right); + } + + public static bool operator !=(ComponentData left, ComponentData right) + { + return !Object.Equals(left, right); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs new file mode 100644 index 000000000..ae7a9c046 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -0,0 +1,149 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Linq; + using System.Numerics; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort; + using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; + + internal static partial class LibJpegTools + { + /// + /// Stores spectral jpeg compoent data in libjpeg-compatible style. + /// + public class SpectralData : IEquatable + { + public int ComponentCount { get; private set; } + + public LibJpegTools.ComponentData[] Components { get; private set; } + + internal SpectralData(LibJpegTools.ComponentData[] components) + { + this.ComponentCount = components.Length; + this.Components = components; + } + + public static SpectralData LoadFromImageSharpDecoder(PdfJsJpegDecoderCore decoder) + { + PdfJsFrameComponent[] srcComponents = decoder.Frame.Components; + LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); + + return new SpectralData(destComponents); + } + + public static SpectralData LoadFromImageSharpDecoder(OrigJpegDecoderCore decoder) + { + OrigComponent[] srcComponents = decoder.Components; + LibJpegTools.ComponentData[] destComponents = srcComponents.Select(LibJpegTools.ComponentData.Load).ToArray(); + + return new SpectralData(destComponents); + } + + public Image TryCreateRGBSpectralImage() + { + if (this.ComponentCount != 3) return null; + + LibJpegTools.ComponentData c0 = this.Components[0]; + LibJpegTools.ComponentData c1 = this.Components[1]; + LibJpegTools.ComponentData c2 = this.Components[2]; + + if (c0.Size != c1.Size || c1.Size != c2.Size) + { + return null; + } + + Image result = new Image(c0.WidthInBlocks * 8, c0.HeightInBlocks * 8); + + for (int by = 0; by < c0.HeightInBlocks; by++) + { + for (int bx = 0; bx < c0.WidthInBlocks; bx++) + { + this.WriteToImage(bx, by, result); + } + } + return result; + } + + internal void WriteToImage(int bx, int by, Image image) + { + LibJpegTools.ComponentData c0 = this.Components[0]; + LibJpegTools.ComponentData c1 = this.Components[1]; + LibJpegTools.ComponentData c2 = this.Components[2]; + + Block8x8 block0 = c0.SpectralBlocks[bx, by]; + Block8x8 block1 = c1.SpectralBlocks[bx, by]; + Block8x8 block2 = c2.SpectralBlocks[bx, by]; + + float d0 = (c0.MaxVal - c0.MinVal); + float d1 = (c1.MaxVal - c1.MinVal); + float d2 = (c2.MaxVal - c2.MinVal); + + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + float val0 = c0.GetBlockValue(block0, x, y); + float val1 = c0.GetBlockValue(block1, x, y); + float val2 = c0.GetBlockValue(block2, x, y); + + Vector4 v = new Vector4(val0, val1, val2, 1); + Rgba32 color = default(Rgba32); + color.PackFromVector4(v); + + int yy = by * 8 + y; + int xx = bx * 8 + x; + image[xx, yy] = color; + } + } + } + + public bool Equals(SpectralData other) + { + if (Object.ReferenceEquals(null, other)) return false; + if (Object.ReferenceEquals(this, other)) return true; + if (this.ComponentCount != other.ComponentCount) + { + return false; + } + + for (int i = 0; i < this.ComponentCount; i++) + { + LibJpegTools.ComponentData a = this.Components[i]; + LibJpegTools.ComponentData b = other.Components[i]; + if (!a.Equals(b)) return false; + } + return true; + } + + public override bool Equals(object obj) + { + if (Object.ReferenceEquals(null, obj)) return false; + if (Object.ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return this.Equals((SpectralData)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (this.ComponentCount * 397) ^ (this.Components != null ? this.Components[0].GetHashCode() : 0); + } + } + + public static bool operator ==(SpectralData left, SpectralData right) + { + return Object.Equals(left, right); + } + + public static bool operator !=(SpectralData left, SpectralData right) + { + return !Object.Equals(left, right); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs new file mode 100644 index 000000000..587511020 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.cs @@ -0,0 +1,133 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Numerics; + using System.Reflection; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + /// + /// Utilities to read raw libjpeg data for reference conversion. + /// + internal static partial class LibJpegTools + { + public static (double total, double average) CalculateDifference(ComponentData expected, ComponentData actual) + { + BigInteger totalDiff = 0; + if (actual.WidthInBlocks < expected.WidthInBlocks) + { + throw new Exception("actual.WidthInBlocks < expected.WidthInBlocks"); + } + + if (actual.HeightInBlocks < expected.HeightInBlocks) + { + throw new Exception("actual.HeightInBlocks < expected.HeightInBlocks"); + } + + int w = expected.WidthInBlocks; + int h = expected.HeightInBlocks; + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + Block8x8 aa = expected.SpectralBlocks[x, y]; + Block8x8 bb = actual.SpectralBlocks[x, y]; + + long diff = Block8x8.TotalDifference(ref aa, ref bb); + totalDiff += diff; + } + } + + int count = w * h; + double total = (double)totalDiff; + double average = (double)totalDiff / (count * Block8x8.Size); + return (total, average); + } + + private static string DumpToolFullPath => Path.Combine( + TestEnvironment.ToolsDirectoryFullPath, + @"jpeg\dump-jpeg-coeffs.exe"); + + /// + /// Executes 'dump-jpeg-coeffs.exe' for the given jpeg image file, saving the libjpeg spectral data into 'destFile'. Windows only! + /// See: + /// + /// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md + /// + /// + public static void RunDumpJpegCoeffsTool(string sourceFile, string destFile) + { + if (!TestEnvironment.IsWindows) + { + throw new InvalidOperationException("Can't run dump-jpeg-coeffs.exe in non-Windows environment. Skip this test on Linux/Unix!"); + } + + string args = $@"""{sourceFile}"" ""{destFile}"""; + var process = Process.Start(DumpToolFullPath, args); + process.WaitForExit(); + } + + /// + /// Extract libjpeg from the given jpg file with 'dump-jpeg-coeffs.exe'. Windows only! + /// See: + /// https://github.com/SixLabors/Imagesharp.Tests.Images/blob/master/tools/jpeg/README.md + /// + public static SpectralData ExtractSpectralData(string inputFile) + { + TestFile testFile = TestFile.Create(inputFile); + + string outDir = TestEnvironment.CreateOutputDirectory(".Temp", $"JpegCoeffs"); + string fn = $"{Path.GetFileName(inputFile)}-{new Random().Next(1000)}.dctcoeffs"; + string coeffFileFullPath = Path.Combine(outDir, fn); + + try + { + RunDumpJpegCoeffsTool(testFile.FullPath, coeffFileFullPath); + + using (var dumpStream = new FileStream(coeffFileFullPath, FileMode.Open)) + using (var rdr = new BinaryReader(dumpStream)) + { + int componentCount = rdr.ReadInt16(); + ComponentData[] result = new ComponentData[componentCount]; + + for (int i = 0; i < componentCount; i++) + { + int widthInBlocks = rdr.ReadInt16(); + int heightInBlocks = rdr.ReadInt16(); + ComponentData resultComponent = new ComponentData(widthInBlocks, heightInBlocks, i); + result[i] = resultComponent; + } + + byte[] buffer = new byte[64*sizeof(short)]; + + for (int i = 0; i < result.Length; i++) + { + ComponentData c = result[i]; + + for (int y = 0; y < c.HeightInBlocks; y++) + { + for (int x = 0; x < c.WidthInBlocks; x++) + { + rdr.Read(buffer, 0, buffer.Length); + + short[] block = buffer.AsSpan().NonPortableCast().ToArray(); + c.MakeBlock(block, y, x); + } + } + } + + return new SpectralData(result); + } + } + finally + { + if (File.Exists(coeffFileFullPath)) + { + File.Delete(coeffFileFullPath); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs new file mode 100644 index 000000000..6a1e09a9b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs @@ -0,0 +1,122 @@ +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + internal static partial class ReferenceImplementations + { + /// + /// True accurate FDCT/IDCT implementations. We should test everything against them! + /// Based on: + /// https://github.com/keithw/mympeg2enc/blob/master/idct.c#L222 + /// Claiming: + /// /* reference idct taken from "ieeetest.c" + /// * Written by Tom Lane (tgl@cs.cmu.edu). + /// * Released to public domain 11/22/93. + /// */ + /// + internal static class AccurateDCT + { + private static double[,] CosLut = InitCosLut(); + + public static Block8x8 TransformIDCT(ref Block8x8 block) + { + Block8x8F temp = block.AsFloatBlock(); + Block8x8F res0 = TransformIDCT(ref temp); + return res0.RoundAsInt16Block(); + } + + public static void TransformIDCTInplace(Span span) + { + var temp = new Block8x8(); + temp.LoadFrom(span); + Block8x8 result = TransformIDCT(ref temp); + result.CopyTo(span); + } + + public static Block8x8 TransformFDCT(ref Block8x8 block) + { + Block8x8F temp = block.AsFloatBlock(); + Block8x8F res0 = TransformFDCT(ref temp); + return res0.RoundAsInt16Block(); + } + + public static void TransformFDCTInplace(Span span) + { + var temp = new Block8x8(); + temp.LoadFrom(span); + Block8x8 result = TransformFDCT(ref temp); + result.CopyTo(span); + } + + public static Block8x8F TransformIDCT(ref Block8x8F block) + { + int x, y, u, v; + double tmp, tmp2; + Block8x8F res = default(Block8x8F); + + for (y=0; y<8; y++) { + for (x=0; x<8; x++) { + tmp = 0.0; + for (v=0; v<8; v++) { + tmp2 = 0.0; + for (u=0; u<8; u++) { + tmp2 += (double) block[v * 8 + u] * CosLut[x, u]; + } + tmp += CosLut[y, v] * tmp2; + } + res[y * 8 + x] = (float)tmp; + } + } + return res; + } + + public static Block8x8F TransformFDCT(ref Block8x8F block) + { + int x, y, u, v; + double tmp, tmp2; + Block8x8F res = default(Block8x8F); + + for (v = 0; v < 8; v++) + { + for (u = 0; u < 8; u++) + { + tmp = 0.0; + for (y = 0; y < 8; y++) + { + tmp2 = 0.0; + for (x = 0; x < 8; x++) + { + tmp2 += (double)block[y * 8 + x] * CosLut[x,u]; + } + tmp += CosLut[y, v] * tmp2; + } + res[v * 8 + u] = (float) tmp; + } + } + + return res; + } + + private static double[,] InitCosLut() + { + double[,] coslu = new double[8, 8]; + int a, b; + double tmp; + + for (a = 0; a < 8; a++) + for (b = 0; b < 8; b++) + { + tmp = Math.Cos((double)((a + a + 1) * b) * (3.14159265358979323846 / 16.0)); + if (b == 0) + { + tmp /= Math.Sqrt(2.0); + } + coslu[a, b] = tmp * 0.5; + } + return coslu; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.GT_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.GT_FloatingPoint_DCT.cs new file mode 100644 index 000000000..2e2f12fbc --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.GT_FloatingPoint_DCT.cs @@ -0,0 +1,69 @@ +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + + internal static partial class ReferenceImplementations + { + /// + /// Non-optimized method ported from: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L446 + /// + /// *** Paper *** + /// Plonka, Gerlind, and Manfred Tasche. "Fast and numerically stable algorithms for discrete cosine transforms." Linear algebra and its applications 394 (2005) : 309 - 345. + /// + internal static class GT_FloatingPoint_DCT + { + public static void idct81d_GT(Span src, Span dst) + { + for (int i = 0; i < 8; i++) + { + float mx00 = 1.4142135623731f * src[0]; + float mx01 = 1.38703984532215f * src[1] + 0.275899379282943f * src[7]; + float mx02 = 1.30656296487638f * src[2] + 0.541196100146197f * src[6]; + float mx03 = 1.17587560241936f * src[3] + 0.785694958387102f * src[5]; + float mx04 = 1.4142135623731f * src[4]; + float mx05 = -0.785694958387102f * src[3] + 1.17587560241936f * src[5]; + float mx06 = 0.541196100146197f * src[2] - 1.30656296487638f * src[6]; + float mx07 = -0.275899379282943f * src[1] + 1.38703984532215f * src[7]; + float mx09 = mx00 + mx04; + float mx0a = mx01 + mx03; + float mx0b = 1.4142135623731f * mx02; + float mx0c = mx00 - mx04; + float mx0d = mx01 - mx03; + float mx0e = 0.353553390593274f * (mx09 - mx0b); + float mx0f = 0.353553390593274f * (mx0c + mx0d); + float mx10 = 0.353553390593274f * (mx0c - mx0d); + float mx11 = 1.4142135623731f * mx06; + float mx12 = mx05 + mx07; + float mx13 = mx05 - mx07; + float mx14 = 0.353553390593274f * (mx11 + mx12); + float mx15 = 0.353553390593274f * (mx11 - mx12); + float mx16 = 0.5f * mx13; + dst[0] = 0.25f * (mx09 + mx0b) + 0.353553390593274f * mx0a; + dst[1] = 0.707106781186547f * (mx0f + mx15); + dst[2] = 0.707106781186547f * (mx0f - mx15); + dst[3] = 0.707106781186547f * (mx0e + mx16); + dst[4] = 0.707106781186547f * (mx0e - mx16); + dst[5] = 0.707106781186547f * (mx10 - mx14); + dst[6] = 0.707106781186547f * (mx10 + mx14); + dst[7] = 0.25f * (mx09 + mx0b) - 0.353553390593274f * mx0a; + dst = dst.Slice(8); + src = src.Slice(8); + } + } + + public static void iDCT8x8GT(Span s, Span d) + { + idct81d_GT(s, d); + + Transpose8x8(d); + + idct81d_GT(d, d); + + Transpose8x8(d); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs new file mode 100644 index 000000000..ef9a73d12 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.LLM_FloatingPoint_DCT.cs @@ -0,0 +1,558 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils; + + using Xunit.Abstractions; + + internal static partial class ReferenceImplementations + { + /// + /// Contains port of non-optimized methods in: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp + /// + /// *** Paper *** + /// paper LLM89 + /// C. Loeffler, A. Ligtenberg, and G. S. Moschytz, + /// "Practical fast 1-D DCT algorithms with 11 multiplications," + /// Proc. Int'l. Conf. on Acoustics, Speech, and Signal Processing (ICASSP89), pp. 988-991, 1989. + /// + /// The main purpose of this code is testing and documentation, it is intented to be similar to it's original counterpart. + /// DO NOT clean it! + /// DO NOT StyleCop it! + /// + internal static class LLM_FloatingPoint_DCT + { + public static Block8x8F TransformIDCT(ref Block8x8F source) + { + float[] s = new float[64]; + source.CopyTo(s); + float[] d = new float[64]; + float[] temp = new float[64]; + + iDCT2D_llm(s, d, temp); + Block8x8F result = default(Block8x8F); + result.LoadFrom(d); + return result; + } + + public static Block8x8F TransformFDCT_UpscaleBy8(ref Block8x8F source) + { + float[] s = new float[64]; + source.CopyTo(s); + float[] d = new float[64]; + float[] temp = new float[64]; + + fDCT2D_llm(s, d, temp); + Block8x8F result = default(Block8x8F); + result.LoadFrom(d); + return result; + } + + private static double cos(double x) => Math.Cos(x); + + private const double M_PI = Math.PI; + + private static readonly double M_SQRT2 = Math.Sqrt(2); + + public static float[] PrintConstants(ITestOutputHelper output) + { + float[] r = new float[8]; + for (int i = 0; i < 8; i++) + { + r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); + output?.WriteLine($"float r{i} = {r[i]:R}f;"); + } + return r; + } + +#pragma warning disable 219 + + /// + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200 + /// + /// + /// + private static void iDCT1Dllm_32f(Span y, Span x) + { + float a0, a1, a2, a3, b0, b1, b2, b3; + float z0, z1, z2, z3, z4; + + // see: PrintConstants() + + float r0 = 1.41421354f; + float r1 = 1.3870399f; + float r2 = 1.306563f; + float r3 = 1.17587554f; + float r4 = 1f; + float r5 = 0.785694957f; + float r6 = 0.5411961f; + float r7 = 0.27589938f; + + z0 = y[1] + y[7]; + z1 = y[3] + y[5]; + z2 = y[3] + y[7]; + z3 = y[1] + y[5]; + z4 = (z0 + z1) * r3; + + z0 = z0 * (-r3 + r7); + z1 = z1 * (-r3 - r1); + z2 = z2 * (-r3 - r5) + z4; + z3 = z3 * (-r3 + r5) + z4; + + b3 = y[7] * (-r1 + r3 + r5 - r7) + z0 + z2; + b2 = y[5] * (r1 + r3 - r5 + r7) + z1 + z3; + b1 = y[3] * (r1 + r3 + r5 - r7) + z1 + z2; + b0 = y[1] * (r1 + r3 - r5 - r7) + z0 + z3; + + z4 = (y[2] + y[6]) * r6; + z0 = y[0] + y[4]; + z1 = y[0] - y[4]; + z2 = z4 - y[6] * (r2 + r6); + z3 = z4 + y[2] * (r2 - r6); + a0 = z0 + z3; + a3 = z0 - z3; + a1 = z1 + z2; + a2 = z1 - z2; + + x[0] = a0 + b0; + x[7] = a0 - b0; + x[1] = a1 + b1; + x[6] = a1 - b1; + x[2] = a2 + b2; + x[5] = a2 - b2; + x[3] = a3 + b3; + x[4] = a3 - b3; + } + + /// + /// Original: https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L239 + /// Applyies IDCT transformation on "s" copying transformed values to "d", using temporal block "temp" + /// + /// + /// + /// + internal static void iDCT2D_llm(Span s, Span d, Span temp) + { + int j; + + for (j = 0; j < 8; j++) + { + iDCT1Dllm_32f(s.Slice(j * 8), temp.Slice(j * 8)); + } + + Transpose8x8(temp, d); + + for (j = 0; j < 8; j++) + { + iDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); + } + + Transpose8x8(temp, d); + + for (j = 0; j < 64; j++) + { + d[j] *= 0.125f; + } + } + + /// + /// Original: + /// + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15 + /// + /// + /// Source + /// Destination + public static void fDCT2D8x4_32f(Span s, Span d) + { + Vector4 c0 = _mm_load_ps(s, 0); + Vector4 c1 = _mm_load_ps(s, 56); + Vector4 t0 = (c0 + c1); + Vector4 t7 = (c0 - c1); + + c1 = _mm_load_ps(s, 48); + c0 = _mm_load_ps(s, 8); + Vector4 t1 = (c0 + c1); + Vector4 t6 = (c0 - c1); + + c1 = _mm_load_ps(s, 40); + c0 = _mm_load_ps(s, 16); + Vector4 t2 = (c0 + c1); + Vector4 t5 = (c0 - c1); + + c0 = _mm_load_ps(s, 24); + c1 = _mm_load_ps(s, 32); + Vector4 t3 = (c0 + c1); + Vector4 t4 = (c0 - c1); + + /* + c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; + c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; + c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; + c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; + */ + + c0 = (t0 + t3); + Vector4 c3 = (t0 - t3); + c1 = (t1 + t2); + Vector4 c2 = (t1 - t2); + + /* + c0 = t0 + t3; c3 = t0 - t3; + c1 = t1 + t2; c2 = t1 - t2; + */ + + _mm_store_ps(d, 0, (c0 + c1)); + + _mm_store_ps(d, 32, (c0 - c1)); + + /*y[0] = c0 + c1; + y[4] = c0 - c1;*/ + + Vector4 w0 = new Vector4(0.541196f); + Vector4 w1 = new Vector4(1.306563f); + + _mm_store_ps(d, 16, ((w0 * c2) + (w1 * c3))); + + _mm_store_ps(d, 48, ((w0 * c3) - (w1 * c2))); + /* + y[2] = c2 * r[6] + c3 * r[2]; + y[6] = c3 * r[6] - c2 * r[2]; + */ + + w0 = new Vector4(1.175876f); + w1 = new Vector4(0.785695f); + c3 = ((w0 * t4) + (w1 * t7)); + c0 = ((w0 * t7) - (w1 * t4)); + /* + c3 = t4 * r[3] + t7 * r[5]; + c0 = t7 * r[3] - t4 * r[5]; + */ + + w0 = new Vector4(1.387040f); + w1 = new Vector4(0.275899f); + c2 = ((w0 * t5) + (w1 * t6)); + c1 = ((w0 * t6) - (w1 * t5)); + /* + c2 = t5 * r[1] + t6 * r[7]; + c1 = t6 * r[1] - t5 * r[7]; + */ + + _mm_store_ps(d, 24, (c0 - c2)); + + _mm_store_ps(d, 40, (c3 - c1)); + //y[5] = c3 - c1; y[3] = c0 - c2; + + Vector4 invsqrt2 = new Vector4(0.707107f); + c0 = ((c0 + c2) * invsqrt2); + c3 = ((c3 + c1) * invsqrt2); + //c0 = (c0 + c2) * invsqrt2; + //c3 = (c3 + c1) * invsqrt2; + + _mm_store_ps(d, 8, (c0 + c3)); + + _mm_store_ps(d, 56, (c0 - c3)); + //y[1] = c0 + c3; y[7] = c0 - c3; + + /*for(i = 0;i < 8;i++) + { + y[i] *= invsqrt2h; + }*/ + } + + public static void fDCT8x8_llm_sse(Span s, Span d, Span temp) + { + ReferenceImplementations.Transpose8x8(s, temp); + + fDCT2D8x4_32f(temp, d); + + fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); + + ReferenceImplementations.Transpose8x8(d, temp); + + fDCT2D8x4_32f(temp, d); + + fDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); + + Vector4 c = new Vector4(0.1250f); + + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//0 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//1 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//2 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//3 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//4 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//5 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//6 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//7 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//8 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//9 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//10 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//11 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//12 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//13 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//14 + _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//15 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 _mm_load_ps(Span src, int offset) + { + src = src.Slice(offset); + return new Vector4(src[0], src[1], src[2], src[3]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void _mm_store_ps(Span dest, int offset, Vector4 src) + { + dest = dest.Slice(offset); + dest[0] = src.X; + dest[1] = src.Y; + dest[2] = src.Z; + dest[3] = src.W; + } + + // Accurate variants of constants from: + // https://github.com/mozilla/mozjpeg/blob/master/simd/jfdctint-altivec.c + + private static readonly Vector4 _1_175876 = new Vector4(1.175875602f); + + private static readonly Vector4 _1_961571 = new Vector4(-1.961570560f); + + private static readonly Vector4 _0_390181 = new Vector4(-0.390180644f); + + private static readonly Vector4 _0_899976 = new Vector4(-0.899976223f); + + private static readonly Vector4 _2_562915 = new Vector4(-2.562915447f); + + private static readonly Vector4 _0_298631 = new Vector4(0.298631336f); + + private static readonly Vector4 _2_053120 = new Vector4(2.053119869f); + + private static readonly Vector4 _3_072711 = new Vector4(3.072711026f); + + private static readonly Vector4 _1_501321 = new Vector4(1.501321110f); + + private static readonly Vector4 _0_541196 = new Vector4(0.541196100f); + + private static readonly Vector4 _1_847759 = new Vector4(-1.847759065f); + + private static readonly Vector4 _0_765367 = new Vector4(0.765366865f); + + /// + /// Original: + /// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261 + /// Does a part of the IDCT job on the given parts of the blocks + /// + /// + /// + internal static void iDCT2D8x4_32f(Span y, Span x) + { + /* + float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; + for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } + */ + /* + 0: 1.414214 + 1: 1.387040 + 2: 1.306563 + 3: + 4: 1.000000 + 5: 0.785695 + 6: + 7: 0.275899 + */ + + Vector4 my1 = _mm_load_ps(y, 8); + Vector4 my7 = _mm_load_ps(y, 56); + Vector4 mz0 = my1 + my7; + + Vector4 my3 = _mm_load_ps(y, 24); + Vector4 mz2 = my3 + my7; + Vector4 my5 = _mm_load_ps(y, 40); + Vector4 mz1 = my3 + my5; + Vector4 mz3 = my1 + my5; + + Vector4 mz4 = ((mz0 + mz1) * _1_175876); + //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; + //z4 = (z0 + z1) * r[3]; + + mz2 = mz2 * _1_961571 + mz4; + mz3 = mz3 * _0_390181 + mz4; + mz0 = mz0 * _0_899976; + mz1 = mz1 * _2_562915; + + /* + -0.899976 + -2.562915 + -1.961571 + -0.390181 + z0 = z0 * (-r[3] + r[7]); + z1 = z1 * (-r[3] - r[1]); + z2 = z2 * (-r[3] - r[5]) + z4; + z3 = z3 * (-r[3] + r[5]) + z4;*/ + + Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; + Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; + Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; + Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; + + /* + 0.298631 + 2.053120 + 3.072711 + 1.501321 + b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; + b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; + b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; + b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; + */ + + Vector4 my2 = _mm_load_ps(y, 16); + Vector4 my6 = _mm_load_ps(y, 48); + mz4 = (my2 + my6) * _0_541196; + Vector4 my0 = _mm_load_ps(y, 0); + Vector4 my4 = _mm_load_ps(y, 32); + mz0 = my0 + my4; + mz1 = my0 - my4; + + mz2 = mz4 + my6 * _1_847759; + mz3 = mz4 + my2 * _0_765367; + + my0 = mz0 + mz3; + my3 = mz0 - mz3; + my1 = mz1 + mz2; + my2 = mz1 - mz2; + /* + 1.847759 + 0.765367 + z4 = (y[2] + y[6]) * r[6]; + z0 = y[0] + y[4]; z1 = y[0] - y[4]; + z2 = z4 - y[6] * (r[2] + r[6]); + z3 = z4 + y[2] * (r[2] - r[6]); + a0 = z0 + z3; a3 = z0 - z3; + a1 = z1 + z2; a2 = z1 - z2; + */ + + _mm_store_ps(x, 0, my0 + mb0); + + _mm_store_ps(x, 56, my0 - mb0); + + _mm_store_ps(x, 8, my1 + mb1); + + _mm_store_ps(x, 48, my1 - mb1); + + _mm_store_ps(x, 16, my2 + mb2); + + _mm_store_ps(x, 40, my2 - mb2); + + _mm_store_ps(x, 24, my3 + mb3); + + _mm_store_ps(x, 32, my3 - mb3); + /* + x[0] = a0 + b0; x[7] = a0 - b0; + x[1] = a1 + b1; x[6] = a1 - b1; + x[2] = a2 + b2; x[5] = a2 - b2; + x[3] = a3 + b3; x[4] = a3 - b3; + for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } + */ + } + + internal static void fDCT1Dllm_32f(Span x, Span y) + { + float t0, t1, t2, t3, t4, t5, t6, t7; + float c0, c1, c2, c3; + float[] r = new float[8]; + + //for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } + r[0] = 1.414214f; + r[1] = 1.387040f; + r[2] = 1.306563f; + r[3] = 1.175876f; + r[4] = 1.000000f; + r[5] = 0.785695f; + r[6] = 0.541196f; + r[7] = 0.275899f; + + const float invsqrt2 = 0.707107f; //(float)(1.0f / M_SQRT2); + //const float invsqrt2h = 0.353554f; //invsqrt2*0.5f; + + c1 = x[0]; + c2 = x[7]; + t0 = c1 + c2; + t7 = c1 - c2; + c1 = x[1]; + c2 = x[6]; + t1 = c1 + c2; + t6 = c1 - c2; + c1 = x[2]; + c2 = x[5]; + t2 = c1 + c2; + t5 = c1 - c2; + c1 = x[3]; + c2 = x[4]; + t3 = c1 + c2; + t4 = c1 - c2; + + c0 = t0 + t3; + c3 = t0 - t3; + c1 = t1 + t2; + c2 = t1 - t2; + + y[0] = c0 + c1; + y[4] = c0 - c1; + y[2] = c2 * r[6] + c3 * r[2]; + y[6] = c3 * r[6] - c2 * r[2]; + + c3 = t4 * r[3] + t7 * r[5]; + c0 = t7 * r[3] - t4 * r[5]; + c2 = t5 * r[1] + t6 * r[7]; + c1 = t6 * r[1] - t5 * r[7]; + + y[5] = c3 - c1; + y[3] = c0 - c2; + c0 = (c0 + c2) * invsqrt2; + c3 = (c3 + c1) * invsqrt2; + y[1] = c0 + c3; + y[7] = c0 - c3; + } + + internal static void fDCT2D_llm( + Span s, + Span d, + Span temp, + bool downscaleBy8 = false, + bool subtract128FromSource = false) + { + Span sWorker = subtract128FromSource ? s.AddScalarToAllValues(-128f) : s; + + for (int j = 0; j < 8; j++) + { + fDCT1Dllm_32f(sWorker.Slice(j * 8), temp.Slice(j * 8)); + } + + ReferenceImplementations.Transpose8x8(temp, d); + + for (int j = 0; j < 8; j++) + { + fDCT1Dllm_32f(d.Slice(j * 8), temp.Slice(j * 8)); + } + + ReferenceImplementations.Transpose8x8(temp, d); + + if (downscaleBy8) + { + for (int j = 0; j < 64; j++) + { + d[j] *= 0.125f; + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs new file mode 100644 index 000000000..9afc4b0b3 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs @@ -0,0 +1,367 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + internal static partial class ReferenceImplementations + { + /// + /// TODO: produces really bad results for bigger values! + /// + /// Contains the "original" golang based DCT/IDCT implementations as reference implementations. + /// 1. ===== Forward DCT ===== + /// **** The original golang source claims: + /// It is based on the code in jfdctint.c from the Independent JPEG Group, + /// found at http://www.ijg.org/files/jpegsrc.v8c.tar.gz. + /// + /// **** Could be found here as well: + /// https://github.com/mozilla/mozjpeg/blob/master/jfdctint.c + /// + /// 2. ===== Inverse DCT ===== + /// + /// The golang source claims: + /// http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_IEC_13818-4_2004_Conformance_Testing/Video/verifier/mpeg2decode_960109.tar.gz + /// The referenced MPEG2 code claims: + /// /**********************************************************/ + /// /* inverse two dimensional DCT, Chen-Wang algorithm */ + /// /* (cf. IEEE ASSP-32, pp. 803-816, Aug. 1984) */ + /// /* 32-bit integer arithmetic (8 bit coefficients) */ + /// /* 11 mults, 29 adds per DCT */ + /// /* sE, 18.8.91 */ + /// /**********************************************************/ + /// /* coefficients extended to 12 bit for IEEE1180-1990 */ + /// /* compliance sE, 2.1.94 */ + /// /**********************************************************/ + /// + /// **** The code looks pretty similar to the standard libjpeg IDCT, but without quantization: + /// https://github.com/mozilla/mozjpeg/blob/master/jidctint.c + /// + public static class StandardIntegerDCT + { + private const int fix_0_298631336 = 2446; + private const int fix_0_390180644 = 3196; + private const int fix_0_541196100 = 4433; + private const int fix_0_765366865 = 6270; + private const int fix_0_899976223 = 7373; + private const int fix_1_175875602 = 9633; + private const int fix_1_501321110 = 12299; + private const int fix_1_847759065 = 15137; + private const int fix_1_961570560 = 16069; + private const int fix_2_053119869 = 16819; + private const int fix_2_562915447 = 20995; + private const int fix_3_072711026 = 25172; + + /// + /// The number of bits + /// + private const int Bits = 13; + + /// + /// The number of bits to shift by on the first pass. + /// + private const int Pass1Bits = 2; + + /// + /// The value to shift by + /// + private const int CenterJSample = 128; + + public static Block8x8 Subtract128_TransformFDCT_Upscale8(ref Block8x8 block) + { + int[] temp = new int[Block8x8.Size]; + block.CopyTo(temp); + Subtract128_TransformFDCT_Upscale8_Inplace(temp); + var result = default(Block8x8); + result.LoadFrom(temp); + return result; + } + + // [Obsolete("Looks like this method produces really bad results for bigger values!")] + public static Block8x8 TransformIDCT(ref Block8x8 block) + { + int[] temp = new int[Block8x8.Size]; + block.CopyTo(temp); + TransformIDCTInplace(temp); + var result = default(Block8x8); + result.LoadFrom(temp); + return result; + } + + /// + /// Performs a forward DCT on an 8x8 block of coefficients, including a level shift. + /// Leave results scaled up by an overall factor of 8. + /// + /// The block of coefficients. + public static void Subtract128_TransformFDCT_Upscale8_Inplace(Span block) + { + // Pass 1: process rows. + for (int y = 0; y < 8; y++) + { + int y8 = y * 8; + + int x0 = block[y8]; + int x1 = block[y8 + 1]; + int x2 = block[y8 + 2]; + int x3 = block[y8 + 3]; + int x4 = block[y8 + 4]; + int x5 = block[y8 + 5]; + int x6 = block[y8 + 6]; + int x7 = block[y8 + 7]; + + int tmp0 = x0 + x7; + int tmp1 = x1 + x6; + int tmp2 = x2 + x5; + int tmp3 = x3 + x4; + + int tmp10 = tmp0 + tmp3; + int tmp12 = tmp0 - tmp3; + int tmp11 = tmp1 + tmp2; + int tmp13 = tmp1 - tmp2; + + tmp0 = x0 - x7; + tmp1 = x1 - x6; + tmp2 = x2 - x5; + tmp3 = x3 - x4; + + block[y8] = (tmp10 + tmp11 - (8 * CenterJSample)) << Pass1Bits; + block[y8 + 4] = (tmp10 - tmp11) << Pass1Bits; + int z1 = (tmp12 + tmp13) * fix_0_541196100; + z1 += 1 << (Bits - Pass1Bits - 1); + block[y8 + 2] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits - Pass1Bits); + block[y8 + 6] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits - Pass1Bits); + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = (tmp12 + tmp13) * fix_1_175875602; + z1 += 1 << (Bits - Pass1Bits - 1); + tmp0 = tmp0 * fix_1_501321110; + tmp1 = tmp1 * fix_3_072711026; + tmp2 = tmp2 * fix_2_053119869; + tmp3 = tmp3 * fix_0_298631336; + tmp10 = tmp10 * -fix_0_899976223; + tmp11 = tmp11 * -fix_2_562915447; + tmp12 = tmp12 * -fix_0_390180644; + tmp13 = tmp13 * -fix_1_961570560; + + tmp12 += z1; + tmp13 += z1; + block[y8 + 1] = (tmp0 + tmp10 + tmp12) >> (Bits - Pass1Bits); + block[y8 + 3] = (tmp1 + tmp11 + tmp13) >> (Bits - Pass1Bits); + block[y8 + 5] = (tmp2 + tmp11 + tmp12) >> (Bits - Pass1Bits); + block[y8 + 7] = (tmp3 + tmp10 + tmp13) >> (Bits - Pass1Bits); + } + + // Pass 2: process columns. + // We remove pass1Bits scaling, but leave results scaled up by an overall factor of 8. + for (int x = 0; x < 8; x++) + { + int tmp0 = block[x] + block[56 + x]; + int tmp1 = block[8 + x] + block[48 + x]; + int tmp2 = block[16 + x] + block[40 + x]; + int tmp3 = block[24 + x] + block[32 + x]; + + int tmp10 = tmp0 + tmp3 + (1 << (Pass1Bits - 1)); + int tmp12 = tmp0 - tmp3; + int tmp11 = tmp1 + tmp2; + int tmp13 = tmp1 - tmp2; + + tmp0 = block[x] - block[56 + x]; + tmp1 = block[8 + x] - block[48 + x]; + tmp2 = block[16 + x] - block[40 + x]; + tmp3 = block[24 + x] - block[32 + x]; + + block[x] = (tmp10 + tmp11) >> Pass1Bits; + block[32 + x] = (tmp10 - tmp11) >> Pass1Bits; + + int z1 = (tmp12 + tmp13) * fix_0_541196100; + z1 += 1 << (Bits + Pass1Bits - 1); + block[16 + x] = (z1 + (tmp12 * fix_0_765366865)) >> (Bits + Pass1Bits); + block[48 + x] = (z1 - (tmp13 * fix_1_847759065)) >> (Bits + Pass1Bits); + + tmp10 = tmp0 + tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp0 + tmp2; + tmp13 = tmp1 + tmp3; + z1 = (tmp12 + tmp13) * fix_1_175875602; + z1 += 1 << (Bits + Pass1Bits - 1); + tmp0 = tmp0 * fix_1_501321110; + tmp1 = tmp1 * fix_3_072711026; + tmp2 = tmp2 * fix_2_053119869; + tmp3 = tmp3 * fix_0_298631336; + tmp10 = tmp10 * -fix_0_899976223; + tmp11 = tmp11 * -fix_2_562915447; + tmp12 = tmp12 * -fix_0_390180644; + tmp13 = tmp13 * -fix_1_961570560; + + tmp12 += z1; + tmp13 += z1; + block[8 + x] = (tmp0 + tmp10 + tmp12) >> (Bits + Pass1Bits); + block[24 + x] = (tmp1 + tmp11 + tmp13) >> (Bits + Pass1Bits); + block[40 + x] = (tmp2 + tmp11 + tmp12) >> (Bits + Pass1Bits); + block[56 + x] = (tmp3 + tmp10 + tmp13) >> (Bits + Pass1Bits); + } + + } + private const int w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16) + private const int w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16) + private const int w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16) + private const int w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16) + private const int w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16) + private const int w7 = 565; // 2048*sqrt(2)*cos(7*pi/16) + + private const int w1pw7 = w1 + w7; + private const int w1mw7 = w1 - w7; + private const int w2pw6 = w2 + w6; + private const int w2mw6 = w2 - w6; + private const int w3pw5 = w3 + w5; + private const int w3mw5 = w3 - w5; + + private const int r2 = 181; // 256/sqrt(2) + + /// + /// Performs a 2-D Inverse Discrete Cosine Transformation. + /// + /// The input coefficients should already have been multiplied by the + /// appropriate quantization table. We use fixed-point computation, with the + /// number of bits for the fractional component varying over the intermediate + /// stages. + /// + /// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the + /// discrete W transform and for the discrete Fourier transform", IEEE Trans. on + /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. + /// + /// The source block of coefficients + // [Obsolete("Looks like this method produces really bad results for bigger values!")] + public static void TransformIDCTInplace(Span src) + { + // Horizontal 1-D IDCT. + for (int y = 0; y < 8; y++) + { + int y8 = y * 8; + + // If all the AC components are zero, then the IDCT is trivial. + if (src[y8 + 1] == 0 && src[y8 + 2] == 0 && src[y8 + 3] == 0 && + src[y8 + 4] == 0 && src[y8 + 5] == 0 && src[y8 + 6] == 0 && src[y8 + 7] == 0) + { + int dc = src[y8 + 0] << 3; + src[y8 + 0] = dc; + src[y8 + 1] = dc; + src[y8 + 2] = dc; + src[y8 + 3] = dc; + src[y8 + 4] = dc; + src[y8 + 5] = dc; + src[y8 + 6] = dc; + src[y8 + 7] = dc; + continue; + } + + // Prescale. + int x0 = (src[y8 + 0] << 11) + 128; + int x1 = src[y8 + 4] << 11; + int x2 = src[y8 + 6]; + int x3 = src[y8 + 2]; + int x4 = src[y8 + 1]; + int x5 = src[y8 + 7]; + int x6 = src[y8 + 5]; + int x7 = src[y8 + 3]; + + // Stage 1. + int x8 = w7 * (x4 + x5); + x4 = x8 + (w1mw7 * x4); + x5 = x8 - (w1pw7 * x5); + x8 = w3 * (x6 + x7); + x6 = x8 - (w3mw5 * x6); + x7 = x8 - (w3pw5 * x7); + + // Stage 2. + x8 = x0 + x1; + x0 -= x1; + x1 = w6 * (x3 + x2); + x2 = x1 - (w2pw6 * x2); + x3 = x1 + (w2mw6 * x3); + x1 = x4 + x6; + x4 -= x6; + x6 = x5 + x7; + x5 -= x7; + + // Stage 3. + x7 = x8 + x3; + x8 -= x3; + x3 = x0 + x2; + x0 -= x2; + x2 = ((r2 * (x4 + x5)) + 128) >> 8; + x4 = ((r2 * (x4 - x5)) + 128) >> 8; + + // Stage 4. + src[y8 + 0] = (x7 + x1) >> 8; + src[y8 + 1] = (x3 + x2) >> 8; + src[y8 + 2] = (x0 + x4) >> 8; + src[y8 + 3] = (x8 + x6) >> 8; + src[y8 + 4] = (x8 - x6) >> 8; + src[y8 + 5] = (x0 - x4) >> 8; + src[y8 + 6] = (x3 - x2) >> 8; + src[y8 + 7] = (x7 - x1) >> 8; + } + + // Vertical 1-D IDCT. + for (int x = 0; x < 8; x++) + { + // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial. + // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so + // we do not bother to check for the all-zero case. + + // Prescale. + int y0 = (src[x] << 8) + 8192; + int y1 = src[32 + x] << 8; + int y2 = src[48 + x]; + int y3 = src[16 + x]; + int y4 = src[8 + x]; + int y5 = src[56 + x]; + int y6 = src[40 + x]; + int y7 = src[24 + x]; + + // Stage 1. + int y8 = (w7 * (y4 + y5)) + 4; + y4 = (y8 + (w1mw7 * y4)) >> 3; + y5 = (y8 - (w1pw7 * y5)) >> 3; + y8 = (w3 * (y6 + y7)) + 4; + y6 = (y8 - (w3mw5 * y6)) >> 3; + y7 = (y8 - (w3pw5 * y7)) >> 3; + + // Stage 2. + y8 = y0 + y1; + y0 -= y1; + y1 = (w6 * (y3 + y2)) + 4; + y2 = (y1 - (w2pw6 * y2)) >> 3; + y3 = (y1 + (w2mw6 * y3)) >> 3; + y1 = y4 + y6; + y4 -= y6; + y6 = y5 + y7; + y5 -= y7; + + // Stage 3. + y7 = y8 + y3; + y8 -= y3; + y3 = y0 + y2; + y0 -= y2; + y2 = ((r2 * (y4 + y5)) + 128) >> 8; + y4 = ((r2 * (y4 - y5)) + 128) >> 8; + + // Stage 4. + src[x] = (y7 + y1) >> 14; + src[8 + x] = (y3 + y2) >> 14; + src[16 + x] = (y0 + y4) >> 14; + src[24 + x] = (y8 + y6) >> 14; + src[32 + x] = (y8 - y6) >> 14; + src[40 + x] = (y0 - y4) >> 14; + src[48 + x] = (y3 - y2) >> 14; + src[56 + x] = (y7 - y1) >> 14; + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs new file mode 100644 index 000000000..c8240bf08 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs @@ -0,0 +1,135 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + + + +// ReSharper disable InconsistentNaming + + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Formats.Jpeg.Common; + + /// + /// This class contains simplified (unefficient) reference implementations to produce verification data for unit tests + /// Floating point DCT code Ported from https://github.com/norishigefukushima/dct_simd + /// + internal static partial class ReferenceImplementations + { + /// + /// Transpose 8x8 block stored linearly in a (inplace) + /// + /// + internal static void Transpose8x8(Span data) + { + for (int i = 1; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < i; j++) + { + float tmp = data[i8 + j]; + data[i8 + j] = data[j * 8 + i]; + data[j * 8 + i] = tmp; + } + } + } + + /// + /// Transpose 8x8 block stored linearly in a + /// + internal static void Transpose8x8(Span src, Span dest) + { + for (int i = 0; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < 8; j++) + { + dest[j * 8 + i] = src[i8 + j]; + } + } + } + + /// + /// Copies color values from block to the destination image buffer. + /// + /// + /// + /// + internal static unsafe void CopyColorsTo(ref Block8x8F block, Span buffer, int stride) + { + fixed (Block8x8F* p = &block) + { + float* b = (float*)p; + + for (int y = 0; y < 8; y++) + { + int y8 = y * 8; + int yStride = y * stride; + + for (int x = 0; x < 8; x++) + { + float c = b[y8 + x]; + + if (c < -128) + { + c = 0; + } + else if (c > 127) + { + c = 255; + } + else + { + c += 128; + } + + buffer[yStride + x] = (byte)c; + } + } + } + } + + /// + /// Reference implementation to test . + /// Rounding is done used an integer-based algorithm defined in . + /// + /// The input block + /// The destination block of integers + /// The quantization table + /// Pointer to + public static unsafe void UnZigDivRoundRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr) + { + float* s = (float*)src; + float* q = (float*)qt; + + for (int zig = 0; zig < Block8x8F.Size; zig++) + { + int a = (int)s[unzigPtr[zig]]; + int b = (int)q[zig]; + + int val = RationalRound(a, b); + dest[zig] = val; + } + } + + /// + /// Rounds a rational number defined as dividend/divisor into an integer + /// + /// The dividend + /// The divisior + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RationalRound(int dividend, int divisor) + { + if (dividend >= 0) + { + return (dividend + (divisor >> 1)) / divisor; + } + + return -((-dividend + (divisor >> 1)) / divisor); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs new file mode 100644 index 000000000..988b9e478 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/SpanExtensions.cs @@ -0,0 +1,121 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Span Extensions + /// + internal static class SpanExtensions + { + /// + /// Save to a Vector4 + /// + /// The data + /// The vector to save to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SaveTo(this Span data, ref Vector4 v) + { + v.X = data[0]; + v.Y = data[1]; + v.Z = data[2]; + v.W = data[3]; + } + + /// + /// Save to a Vector4 + /// + /// The data + /// The vector to save to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SaveTo(this Span data, ref Vector4 v) + { + v.X = data[0]; + v.Y = data[1]; + v.Z = data[2]; + v.W = data[3]; + } + + /// + /// Load from Vector4 + /// + /// The data + /// The vector to load from + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LoadFrom(this Span data, ref Vector4 v) + { + data[0] = v.X; + data[1] = v.Y; + data[2] = v.Z; + data[3] = v.W; + } + + /// + /// Load from Vector4 + /// + /// The data + /// The vector to load from + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LoadFrom(this Span data, ref Vector4 v) + { + data[0] = (int)v.X; + data[1] = (int)v.Y; + data[2] = (int)v.Z; + data[3] = (int)v.W; + } + + /// + /// Converts all int values of src to float + /// + /// Source + /// A new with float values + public static float[] ConvertAllToFloat(this int[] src) + { + float[] result = new float[src.Length]; + for (int i = 0; i < src.Length; i++) + { + result[i] = (float)src[i]; + } + + return result; + } + + /// + /// Add a scalar to all values of src + /// + /// The source + /// The scalar value to add + /// A new instance of + public static Span AddScalarToAllValues(this Span src, float scalar) + { + float[] result = new float[src.Length]; + for (int i = 0; i < src.Length; i++) + { + result[i] = src[i] + scalar; + } + + return result; + } + + /// + /// Add a scalar to all values of src + /// + /// The source + /// The scalar value to add + /// A new instance of + public static Span AddScalarToAllValues(this Span src, int scalar) + { + int[] result = new int[src.Length]; + for (int i = 0; i < src.Length; i++) + { + result[i] = src[i] + scalar; + } + + return result; + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs new file mode 100644 index 000000000..d0f7df12c --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/VerifyJpeg.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; + +using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils +{ + internal static class VerifyJpeg + { + internal static void VerifySize(IJpegComponent component, int expectedBlocksX, int expectedBlocksY) + { + Assert.Equal(new Size(expectedBlocksX, expectedBlocksY), component.SizeInBlocks); + } + + internal static void VerifyComponent( + IJpegComponent component, + Size expectedSizeInBlocks, + Size expectedSamplingFactors, + Size expectedSubsamplingDivisors) + { + Assert.Equal(expectedSizeInBlocks, component.SizeInBlocks); + Assert.Equal(expectedSamplingFactors, component.SamplingFactors); + Assert.Equal(expectedSubsamplingDivisors, component.SubSamplingDivisors); + } + + internal static void VerifyComponentSizes3( + IEnumerable components, + int xBc0, + int yBc0, + int xBc1, + int yBc1, + int xBc2, + int yBc2) + { + IJpegComponent[] c = components.ToArray(); + Assert.Equal(3, components.Count()); + + VerifySize(c[0], xBc0, yBc0); + VerifySize(c[1], xBc1, yBc1); + VerifySize(c[2], xBc2, yBc2); + } + + internal static void SaveSpectralImage( + TestImageProvider provider, + LibJpegTools.SpectralData data, + ITestOutputHelper output = null) + where TPixel : struct, IPixel + { + foreach (LibJpegTools.ComponentData comp in data.Components) + { + output?.WriteLine("Min: " + comp.MinVal); + output?.WriteLine("Max: " + comp.MaxVal); + + using (Image image = comp.CreateGrayScaleImage()) + { + string details = $"C{comp.Index}"; + image.DebugSave(provider, details, appendPixelTypeToFileName: false); + } + } + + Image fullImage = data.TryCreateRGBSpectralImage(); + + if (fullImage != null) + { + fullImage.DebugSave(provider, "FULL", appendPixelTypeToFileName: false); + fullImage.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs deleted file mode 100644 index b911e1184..000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using ImageSharp.Formats.Jpg; - using SixLabors.Primitives; - using Xunit; - using Xunit.Abstractions; - - public class YCbCrImageTests - { - public YCbCrImageTests(ITestOutputHelper output) - { - this.Output = output; - } - - private ITestOutputHelper Output { get; } - - [Theory] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410, 4, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411, 4, 1)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420, 2, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422, 2, 1)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440, 1, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444, 1, 1)] - internal void CalculateChrominanceSize( - YCbCrImage.YCbCrSubsampleRatio ratioValue, - int expectedDivX, - int expectedDivY) - { - YCbCrImage.YCbCrSubsampleRatio ratio = (YCbCrImage.YCbCrSubsampleRatio)ratioValue; - - //this.Output.WriteLine($"RATIO: {ratio}"); - Size size = YCbCrImage.CalculateChrominanceSize(400, 400, ratio); - //this.Output.WriteLine($"Ch Size: {size}"); - - Assert.Equal(new Size(400 / expectedDivX, 400 / expectedDivY), size); - } - - [Theory] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410, 4)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411, 4)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio420, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422, 2)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440, 1)] - [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444, 1)] - internal void Create(YCbCrImage.YCbCrSubsampleRatio ratioValue, int expectedCStrideDiv) - { - YCbCrImage.YCbCrSubsampleRatio ratio = (YCbCrImage.YCbCrSubsampleRatio)ratioValue; - - this.Output.WriteLine($"RATIO: {ratio}"); - - YCbCrImage img = new YCbCrImage(400, 400, ratio); - - //this.PrintChannel("Y", img.YChannel); - //this.PrintChannel("Cb", img.CbChannel); - //this.PrintChannel("Cr", img.CrChannel); - - Assert.Equal(400, img.YChannel.Width); - Assert.Equal(img.CbChannel.Width, 400 / expectedCStrideDiv); - Assert.Equal(img.CrChannel.Width, 400 / expectedCStrideDiv); - } - - private void PrintChannel(string name, JpegPixelArea channel) - { - this.Output.WriteLine($"{name}: Stride={channel.Stride}"); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ycck.jpg b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/baseline/ycck - Copy.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ycck.jpg rename to tests/ImageSharp.Tests/Formats/Jpg/pdfjs/baseline/ycck - Copy.jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm new file mode 100644 index 000000000..40af957fc --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg-converter.htm @@ -0,0 +1,89 @@ + + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm new file mode 100644 index 000000000..72a5e448b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpeg.htm @@ -0,0 +1,63 @@ + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js new file mode 100644 index 000000000..6ebf71a69 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/pdfjs/jpg.js @@ -0,0 +1,1205 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-disable no-multi-spaces */ + +// import { error, warn } from '../shared/util'; + +/** + * This code was forked from https://github.com/notmasteryet/jpgjs. + * The original version was created by GitHub user notmasteryet. + * + * - The JPEG specification can be found in the ITU CCITT Recommendation T.81 + * (www.w3.org/Graphics/JPEG/itu-t81.pdf) + * - The JFIF specification can be found in the JPEG File Interchange Format + * (www.w3.org/Graphics/JPEG/jfif3.pdf) + * - The Adobe Application-Specific JPEG markers in the + * Supporting the DCT Filters in PostScript Level 2, Technical Note #5116 + * (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf) + */ + +var error = function(val){ + console.log(val); +} + +var warn = function(val){ + console.log(val); +} + +var JpegImage = (function JpegImageClosure() { + var dctZigZag = new Uint8Array([ + 0, + 1, 8, + 16, 9, 2, + 3, 10, 17, 24, + 32, 25, 18, 11, 4, + 5, 12, 19, 26, 33, 40, + 48, 41, 34, 27, 20, 13, 6, + 7, 14, 21, 28, 35, 42, 49, 56, + 57, 50, 43, 36, 29, 22, 15, + 23, 30, 37, 44, 51, 58, + 59, 52, 45, 38, 31, + 39, 46, 53, 60, + 61, 54, 47, + 55, 62, + 63 + ]); + + var dctCos1 = 4017; // cos(pi/16) + var dctSin1 = 799; // sin(pi/16) + var dctCos3 = 3406; // cos(3*pi/16) + var dctSin3 = 2276; // sin(3*pi/16) + var dctCos6 = 1567; // cos(6*pi/16) + var dctSin6 = 3784; // sin(6*pi/16) + var dctSqrt2 = 5793; // sqrt(2) + var dctSqrt1d2 = 2896; // sqrt(2) / 2 + + function JpegImage() { + this.decodeTransform = null; + this.colorTransform = -1; + } + + function buildHuffmanTable(codeLengths, values) { + console.log(codeLengths); + console.log(values); + + var k = 0, code = [], i, j, length = 16; + while (length > 0 && !codeLengths[length - 1]) { + length--; + } + + code.push({ children: [], index: 0, }); + var p = code[0], q; + for (i = 0; i < length; i++) { + for (j = 0; j < codeLengths[i]; j++) { + p = code.pop(); + p.children[p.index] = values[k]; + + while (p.index > 0) { + p = code.pop(); + } + p.index++; + code.push(p); + while (code.length <= i) { + code.push(q = { children: [], index: 0, }); + p.children[p.index] = q.children; + p = q; + } + k++; + } + if (i + 1 < length) { + // p here points to last code + code.push(q = { children: [], index: 0, }); + p.children[p.index] = q.children; + p = q; + } + } + console.log(code[0].children); + console.log(k); + return code[0].children; + } + + function getBlockBufferOffset(component, row, col) { + return 64 * ((component.blocksPerLine + 1) * row + col); + } + + function decodeScan(data, offset, frame, components, resetInterval, + spectralStart, spectralEnd, successivePrev, successive) { + var mcusPerLine = frame.mcusPerLine; + var progressive = frame.progressive; + var startOffset = offset, bitsData = 0, bitsCount = 0; + + function readBit() { + if (bitsCount > 0) { + bitsCount--; + return (bitsData >> bitsCount) & 1; + } + bitsData = data[offset++]; + if (bitsData === 0xFF) { + var nextByte = data[offset++]; + if (nextByte) { + error('JPEG error: unexpected marker ' + + ((bitsData << 8) | nextByte).toString(16)); + } + // unstuff 0 + } + bitsCount = 7; + return bitsData >>> 7; + } + + function decodeHuffman(tree) { + var node = tree; + while (true) { + node = node[readBit()]; + if (typeof node === 'number') { + return node; + } + if (typeof node !== 'object') { + error('JPEG error: invalid huffman sequence'); + } + } + } + + function receive(length) { + var n = 0; + while (length > 0) { + n = (n << 1) | readBit(); + length--; + } + return n; + } + + function receiveAndExtend(length) { + if (length === 1) { + return readBit() === 1 ? 1 : -1; + } + var n = receive(length); + if (n >= 1 << (length - 1)) { + return n; + } + return n + (-1 << length) + 1; + } + + function decodeBaseline(component, offset) { + var t = decodeHuffman(component.huffmanTableDC); + var diff = t === 0 ? 0 : receiveAndExtend(t); + component.blockData[offset] = (component.pred += diff); + // console.log("component"); + // console.log(component); + + if(offset === 0){ + console.log("component at 0"); + console.log(component.blockData[offset]) + } + + var k = 1; + while (k < 64) { + var rs = decodeHuffman(component.huffmanTableAC); + var s = rs & 15, r = rs >> 4; + if (s === 0) { + if (r < 15) { + break; + } + k += 16; + continue; + } + k += r; + var z = dctZigZag[k]; + component.blockData[offset + z] = receiveAndExtend(s); + k++; + } + } + + function decodeDCFirst(component, offset) { + var t = decodeHuffman(component.huffmanTableDC); + var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive); + component.blockData[offset] = (component.pred += diff); + } + + function decodeDCSuccessive(component, offset) { + component.blockData[offset] |= readBit() << successive; + } + + var eobrun = 0; + function decodeACFirst(component, offset) { + if (eobrun > 0) { + eobrun--; + return; + } + var k = spectralStart, e = spectralEnd; + while (k <= e) { + var rs = decodeHuffman(component.huffmanTableAC); + var s = rs & 15, r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r) - 1; + break; + } + k += 16; + continue; + } + k += r; + var z = dctZigZag[k]; + component.blockData[offset + z] = + receiveAndExtend(s) * (1 << successive); + k++; + } + } + + var successiveACState = 0, successiveACNextValue; + function decodeACSuccessive(component, offset) { + var k = spectralStart; + var e = spectralEnd; + var r = 0; + var s; + var rs; + while (k <= e) { + var z = dctZigZag[k]; + switch (successiveACState) { + case 0: // initial state + rs = decodeHuffman(component.huffmanTableAC); + s = rs & 15; + r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r); + successiveACState = 4; + } else { + r = 16; + successiveACState = 1; + } + } else { + if (s !== 1) { + error('JPEG error: invalid ACn encoding'); + } + successiveACNextValue = receiveAndExtend(s); + successiveACState = r ? 2 : 3; + } + continue; + case 1: // skipping r zero items + case 2: + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } else { + r--; + if (r === 0) { + successiveACState = successiveACState === 2 ? 3 : 0; + } + } + break; + case 3: // set value for a zero item + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } else { + component.blockData[offset + z] = + successiveACNextValue << successive; + successiveACState = 0; + } + break; + case 4: // eob + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } + break; + } + k++; + } + if (successiveACState === 4) { + eobrun--; + if (eobrun === 0) { + successiveACState = 0; + } + } + } + + function decodeMcu(component, decode, mcu, row, col) { + var mcuRow = (mcu / mcusPerLine) | 0; + var mcuCol = mcu % mcusPerLine; + var blockRow = mcuRow * component.v + row; + var blockCol = mcuCol * component.h + col; + var offset = getBlockBufferOffset(component, blockRow, blockCol); + + // console.log("MCU Offset: " + offset); + decode(component, offset); + } + + function decodeBlock(component, decode, mcu) { + var blockRow = (mcu / component.blocksPerLine) | 0; + var blockCol = mcu % component.blocksPerLine; + var offset = getBlockBufferOffset(component, blockRow, blockCol); + decode(component, offset); + } + + var componentsLength = components.length; + var component, i, j, k, n; + var decodeFn; + if (progressive) { + if (spectralStart === 0) { + decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive; + console.log(successivePrev === 0 ? "decodeDCFirst" : "decodeDCSuccessive"); + } else { + decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive; + console.log(successivePrev === 0 ? "decodeACFirst" : "decodeACSuccessive"); + } + } else { + decodeFn = decodeBaseline; + } + + var mcu = 0, fileMarker; + var mcuExpected; + if (componentsLength === 1) { + mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn; + } else { + mcuExpected = mcusPerLine * frame.mcusPerColumn; + } + + console.log("mcuExpected = "+ mcuExpected); + + var h, v; + while (mcu < mcuExpected) { + // reset interval stuff + var mcuToRead = resetInterval ? + Math.min(mcuExpected - mcu, resetInterval) : mcuExpected; + for (i = 0; i < componentsLength; i++) { + components[i].pred = 0; + } + eobrun = 0; + + if (componentsLength === 1) { + component = components[0]; + + for (n = 0; n < mcuToRead; n++) { + decodeBlock(component, decodeFn, mcu); + mcu++; + } + } else { + for (n = 0; n < mcuToRead; n++) { + for (i = 0; i < componentsLength; i++) { + component = components[i]; + h = component.h; + v = component.v; + for (j = 0; j < v; j++) { + for (k = 0; k < h; k++) { + decodeMcu(component, decodeFn, mcu, j, k); + } + } + } + mcu++; + } + } + + // find marker + bitsCount = 0; + fileMarker = findNextFileMarker(data, offset); + // Some bad images seem to pad Scan blocks with e.g. zero bytes, skip past + // those to attempt to find a valid marker (fixes issue4090.pdf). + if (fileMarker && fileMarker.invalid) { + warn('decodeScan - unexpected MCU data, next marker is: ' + + fileMarker.invalid); + offset = fileMarker.offset; + } + var marker = fileMarker && fileMarker.marker; + if (!marker || marker <= 0xFF00) { + error('JPEG error: marker was not found'); + } + + if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx + offset += 2; + } else { + break; + } + } + + fileMarker = findNextFileMarker(data, offset); + // Some images include more Scan blocks than expected, skip past those and + // attempt to find the next valid marker (fixes issue8182.pdf). + if (fileMarker && fileMarker.invalid) { + warn('decodeScan - unexpected Scan data, next marker is: ' + + fileMarker.invalid); + offset = fileMarker.offset; + } + + return offset - startOffset; + } + + // A port of poppler's IDCT method which in turn is taken from: + // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, + // 'Practical Fast 1-D DCT Algorithms with 11 Multiplications', + // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, + // 988-991. + function quantizeAndInverse(component, blockBufferOffset, p) { + var qt = component.quantizationTable, blockData = component.blockData; + var v0, v1, v2, v3, v4, v5, v6, v7; + var p0, p1, p2, p3, p4, p5, p6, p7; + var t; + + if (!qt) { + error('JPEG error: missing required Quantization Table.'); + } + + // inverse DCT on rows + for (var row = 0; row < 64; row += 8) { + // gather block data + p0 = blockData[blockBufferOffset + row]; + p1 = blockData[blockBufferOffset + row + 1]; + p2 = blockData[blockBufferOffset + row + 2]; + p3 = blockData[blockBufferOffset + row + 3]; + p4 = blockData[blockBufferOffset + row + 4]; + p5 = blockData[blockBufferOffset + row + 5]; + p6 = blockData[blockBufferOffset + row + 6]; + p7 = blockData[blockBufferOffset + row + 7]; + + // dequant p0 + p0 *= qt[row]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) { + t = (dctSqrt2 * p0 + 512) >> 10; + p[row] = t; + p[row + 1] = t; + p[row + 2] = t; + p[row + 3] = t; + p[row + 4] = t; + p[row + 5] = t; + p[row + 6] = t; + p[row + 7] = t; + continue; + } + // dequant p1 ... p7 + p1 *= qt[row + 1]; + p2 *= qt[row + 2]; + p3 *= qt[row + 3]; + p4 *= qt[row + 4]; + p5 *= qt[row + 5]; + p6 *= qt[row + 6]; + p7 *= qt[row + 7]; + + // stage 4 + v0 = (dctSqrt2 * p0 + 128) >> 8; + v1 = (dctSqrt2 * p4 + 128) >> 8; + v2 = p2; + v3 = p6; + v4 = (dctSqrt1d2 * (p1 - p7) + 128) >> 8; + v7 = (dctSqrt1d2 * (p1 + p7) + 128) >> 8; + v5 = p3 << 4; + v6 = p5 << 4; + + // stage 3 + v0 = (v0 + v1 + 1) >> 1; + v1 = v0 - v1; + t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p[row] = v0 + v7; + p[row + 7] = v0 - v7; + p[row + 1] = v1 + v6; + p[row + 6] = v1 - v6; + p[row + 2] = v2 + v5; + p[row + 5] = v2 - v5; + p[row + 3] = v3 + v4; + p[row + 4] = v3 - v4; + } + + // inverse DCT on columns + for (var col = 0; col < 8; ++col) { + p0 = p[col]; + p1 = p[col + 8]; + p2 = p[col + 16]; + p3 = p[col + 24]; + p4 = p[col + 32]; + p5 = p[col + 40]; + p6 = p[col + 48]; + p7 = p[col + 56]; + + // check for all-zero AC coefficients + if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) { + t = (dctSqrt2 * p0 + 8192) >> 14; + // convert to 8 bit + t = (t < -2040) ? 0 : (t >= 2024) ? 255 : (t + 2056) >> 4; + blockData[blockBufferOffset + col] = t; + blockData[blockBufferOffset + col + 8] = t; + blockData[blockBufferOffset + col + 16] = t; + blockData[blockBufferOffset + col + 24] = t; + blockData[blockBufferOffset + col + 32] = t; + blockData[blockBufferOffset + col + 40] = t; + blockData[blockBufferOffset + col + 48] = t; + blockData[blockBufferOffset + col + 56] = t; + continue; + } + + // stage 4 + v0 = (dctSqrt2 * p0 + 2048) >> 12; + v1 = (dctSqrt2 * p4 + 2048) >> 12; + v2 = p2; + v3 = p6; + v4 = (dctSqrt1d2 * (p1 - p7) + 2048) >> 12; + v7 = (dctSqrt1d2 * (p1 + p7) + 2048) >> 12; + v5 = p3; + v6 = p5; + + // stage 3 + // Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when + // converting to UInt8 range later. + v0 = ((v0 + v1 + 1) >> 1) + 4112; + v1 = v0 - v1; + t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12; + v3 = t; + v4 = (v4 + v6 + 1) >> 1; + v6 = v4 - v6; + v7 = (v7 + v5 + 1) >> 1; + v5 = v7 - v5; + + // stage 2 + v0 = (v0 + v3 + 1) >> 1; + v3 = v0 - v3; + v1 = (v1 + v2 + 1) >> 1; + v2 = v1 - v2; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p0 = v0 + v7; + p7 = v0 - v7; + p1 = v1 + v6; + p6 = v1 - v6; + p2 = v2 + v5; + p5 = v2 - v5; + p3 = v3 + v4; + p4 = v3 - v4; + + // convert to 8-bit integers + p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? 255 : p0 >> 4; + p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? 255 : p1 >> 4; + p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? 255 : p2 >> 4; + p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? 255 : p3 >> 4; + p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? 255 : p4 >> 4; + p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? 255 : p5 >> 4; + p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? 255 : p6 >> 4; + p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? 255 : p7 >> 4; + + // store block data + blockData[blockBufferOffset + col] = p0; + blockData[blockBufferOffset + col + 8] = p1; + blockData[blockBufferOffset + col + 16] = p2; + blockData[blockBufferOffset + col + 24] = p3; + blockData[blockBufferOffset + col + 32] = p4; + blockData[blockBufferOffset + col + 40] = p5; + blockData[blockBufferOffset + col + 48] = p6; + blockData[blockBufferOffset + col + 56] = p7; + } + } + + function buildComponentData(frame, component) { + var blocksPerLine = component.blocksPerLine; + var blocksPerColumn = component.blocksPerColumn; + var computationBuffer = new Int16Array(64); + console.log("qt"); + console.log(component.quantizationTable); + for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) { + for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) { + var offset = getBlockBufferOffset(component, blockRow, blockCol); + quantizeAndInverse(component, offset, computationBuffer); + } + } + + console.log("component.blockData"); + console.log(component.blockData); + return component.blockData; + } + + function clamp0to255(a) { + return a <= 0 ? 0 : a >= 255 ? 255 : a; + } + + function findNextFileMarker(data, currentPos, startPos) { + function peekUint16(pos) { + return (data[pos] << 8) | data[pos + 1]; + } + + var maxPos = data.length - 1; + var newPos = startPos < currentPos ? startPos : currentPos; + + if (currentPos >= maxPos) { + return null; // Don't attempt to read non-existent data and just return. + } + var currentMarker = peekUint16(currentPos); + if (currentMarker >= 0xFFC0 && currentMarker <= 0xFFFE) { + return { + invalid: null, + marker: currentMarker, + offset: currentPos, + }; + } + var newMarker = peekUint16(newPos); + while (!(newMarker >= 0xFFC0 && newMarker <= 0xFFFE)) { + if (++newPos >= maxPos) { + return null; // Don't attempt to read non-existent data and just return. + } + newMarker = peekUint16(newPos); + } + return { + invalid: currentMarker.toString(16), + marker: newMarker, + offset: newPos, + }; + } + + JpegImage.prototype = { + parse: function parse(data) { + + function readUint16() { + var value = (data[offset] << 8) | data[offset + 1]; + offset += 2; + return value; + } + + function readDataBlock() { + var length = readUint16(); + var endOffset = offset + length - 2; + + var fileMarker = findNextFileMarker(data, endOffset, offset); + if (fileMarker && fileMarker.invalid) { + warn('readDataBlock - incorrect length, next marker is: ' + + fileMarker.invalid); + endOffset = fileMarker.offset; + } + + var array = data.subarray(offset, endOffset); + offset += array.length; + return array; + } + + function prepareComponents(frame) { + var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH); + var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV); + for (var i = 0; i < frame.components.length; i++) { + component = frame.components[i]; + var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * + component.h / frame.maxH); + var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * + component.v / frame.maxV); + var blocksPerLineForMcu = mcusPerLine * component.h; + var blocksPerColumnForMcu = mcusPerColumn * component.v; + + var blocksBufferSize = 64 * blocksPerColumnForMcu * + (blocksPerLineForMcu + 1); + component.blockData = new Int16Array(blocksBufferSize); + component.blocksPerLine = blocksPerLine; + component.blocksPerColumn = blocksPerColumn; + } + frame.mcusPerLine = mcusPerLine; + frame.mcusPerColumn = mcusPerColumn; + } + + var offset = 0; + var jfif = null; + var adobe = null; + var frame, resetInterval; + var quantizationTables = []; + var huffmanTablesAC = [], huffmanTablesDC = []; + var fileMarker = readUint16(); + if (fileMarker !== 0xFFD8) { // SOI (Start of Image) + error('JPEG error: SOI not found'); + } + + fileMarker = readUint16(); + while (fileMarker !== 0xFFD9) { // EOI (End of image) + var i, j, l; + switch (fileMarker) { + case 0xFFE0: // APP0 (Application Specific) + case 0xFFE1: // APP1 + case 0xFFE2: // APP2 + case 0xFFE3: // APP3 + case 0xFFE4: // APP4 + case 0xFFE5: // APP5 + case 0xFFE6: // APP6 + case 0xFFE7: // APP7 + case 0xFFE8: // APP8 + case 0xFFE9: // APP9 + case 0xFFEA: // APP10 + case 0xFFEB: // APP11 + case 0xFFEC: // APP12 + case 0xFFED: // APP13 + case 0xFFEE: // APP14 + case 0xFFEF: // APP15 + case 0xFFFE: // COM (Comment) + var appData = readDataBlock(); + + if (fileMarker === 0xFFE0) { + if (appData[0] === 0x4A && appData[1] === 0x46 && + appData[2] === 0x49 && appData[3] === 0x46 && + appData[4] === 0) { // 'JFIF\x00' + jfif = { + version: { major: appData[5], minor: appData[6], }, + densityUnits: appData[7], + xDensity: (appData[8] << 8) | appData[9], + yDensity: (appData[10] << 8) | appData[11], + thumbWidth: appData[12], + thumbHeight: appData[13], + thumbData: appData.subarray(14, 14 + + 3 * appData[12] * appData[13]), + }; + } + } + // TODO APP1 - Exif + if (fileMarker === 0xFFEE) { + if (appData[0] === 0x41 && appData[1] === 0x64 && + appData[2] === 0x6F && appData[3] === 0x62 && + appData[4] === 0x65) { // 'Adobe' + adobe = { + version: (appData[5] << 8) | appData[6], + flags0: (appData[7] << 8) | appData[8], + flags1: (appData[9] << 8) | appData[10], + transformCode: appData[11], + }; + } + } + break; + + case 0xFFDB: // DQT (Define Quantization Tables) + var quantizationTablesLength = readUint16(); + var quantizationTablesEnd = quantizationTablesLength + offset - 2; + var z; + while (offset < quantizationTablesEnd) { + var quantizationTableSpec = data[offset++]; + var tableData = new Uint16Array(64); + if ((quantizationTableSpec >> 4) === 0) { // 8 bit values + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = data[offset++]; + } + } else if ((quantizationTableSpec >> 4) === 1) { // 16 bit values + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = readUint16(); + } + } else { + error('JPEG error: DQT - invalid table spec'); + } + quantizationTables[quantizationTableSpec & 15] = tableData; + } + break; + + case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT) + case 0xFFC1: // SOF1 (Start of Frame, Extended DCT) + case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT) + if (frame) { + error('JPEG error: Only single frame JPEGs supported'); + } + console.log("filemarker"); + console.log(fileMarker); + console.log(offset); + readUint16(); // skip data length + frame = {}; + frame.extended = (fileMarker === 0xFFC1); + frame.progressive = (fileMarker === 0xFFC2); + frame.precision = data[offset++]; + frame.scanLines = readUint16(); + frame.samplesPerLine = readUint16(); + frame.components = []; + frame.componentIds = {}; + var componentsCount = data[offset++], componentId; + var maxH = 0, maxV = 0; + for (i = 0; i < componentsCount; i++) { + componentId = data[offset]; + var h = data[offset + 1] >> 4; + var v = data[offset + 1] & 15; + if (maxH < h) { + maxH = h; + } + if (maxV < v) { + maxV = v; + } + var qId = data[offset + 2]; + l = frame.components.push({ + h, + v, + quantizationId: qId, + quantizationTable: null, // See comment below. + }); + frame.componentIds[componentId] = l - 1; + offset += 3; + } + + frame.maxH = maxH; + frame.maxV = maxV; + prepareComponents(frame); + break; + + case 0xFFC4: // DHT (Define Huffman Tables) + var huffmanLength = readUint16(); + for (i = 2; i < huffmanLength;) { + console.log("offset= " + offset); + var huffmanTableSpec = data[offset++]; + console.log("huffmanTableSpec= " + huffmanTableSpec); + + var codeLengths = new Uint8Array(16); + var codeLengthSum = 0; + for (j = 0; j < 16; j++, offset++) { + codeLengthSum += (codeLengths[j] = data[offset]); + } + console.log("codelengthsum = " + codeLengthSum); + console.log("offset = " + offset); + var huffmanValues = new Uint8Array(codeLengthSum); + for (j = 0; j < codeLengthSum; j++, offset++) { + huffmanValues[j] = data[offset]; + } + i += 17 + codeLengthSum; + + + console.log((huffmanTableSpec >> 4) === 0 ? "DC":"AC"); + ((huffmanTableSpec >> 4) === 0 + ? huffmanTablesDC + : huffmanTablesAC)[huffmanTableSpec & 15] = + buildHuffmanTable(codeLengths, huffmanValues); + } + break; + + case 0xFFDD: // DRI (Define Restart Interval) + readUint16(); // skip data length + resetInterval = readUint16(); + break; + + case 0xFFDA: // SOS (Start of Scan) + readUint16(); // scanLength + var selectorsCount = data[offset++]; + var components = [], component; + for (i = 0; i < selectorsCount; i++) { + var ci = data[offset++]; + console.log("ci= " + ci); + console.log("offset= " + offset); + + var componentIndex = frame.componentIds[ci]; + console.log("componentIndex= " + componentIndex); + component = frame.components[componentIndex]; + var tableSpec = data[offset++]; + component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4]; + component.huffmanTableAC = huffmanTablesAC[tableSpec & 15]; + components.push(component); + } + console.log("components= " + components); + + var spectralStart = data[offset++]; + var spectralEnd = data[offset++]; + var successiveApproximation = data[offset++]; + + console.log(frame.componentIds); + console.log("spectralStart= " + spectralStart); + console.log("spectralEnd= " + spectralEnd); + console.log("successiveApproximation= " + successiveApproximation); + // console.log("components before") + // console.log(components) + var processed = decodeScan(data, offset, + frame, components, resetInterval, + spectralStart, spectralEnd, + successiveApproximation >> 4, successiveApproximation & 15); + offset += processed; + console.log("components after"); + // console.log(frame); + for (var i = 0; i < 3; i++){ + for (var j = 0; j < 10; j++){ + console.log("component ["+ i +"] : value ["+j+"] ="+ frame.components[i].blockData[j]+"]"); + } + } + break; + + case 0xFFFF: // Fill bytes + if (data[offset] !== 0xFF) { // Avoid skipping a valid marker. + offset--; + } + break; + + default: + if (data[offset - 3] === 0xFF && + data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) { + // could be incorrect encoding -- last 0xFF byte of the previous + // block was eaten by the encoder + offset -= 3; + break; + } + + // TODO: Delete this after testing + fileMarker = 0xFFD9; + // error('JPEG error: unknown marker ' + fileMarker.toString(16)); + } + fileMarker = readUint16(); + } + + console.log("quantizationTables"); + console.log(quantizationTables); + + this.width = frame.samplesPerLine; + this.height = frame.scanLines; + this.jfif = jfif; + this.adobe = adobe; + this.components = []; + for (i = 0; i < frame.components.length; i++) { + component = frame.components[i]; + + // Prevent errors when DQT markers are placed after SOF{n} markers, + // by assigning the `quantizationTable` entry after the entire image + // has been parsed (fixes issue7406.pdf). + var quantizationTable = quantizationTables[component.quantizationId]; + if (quantizationTable) { + component.quantizationTable = quantizationTable; + } + + this.components.push({ + output: buildComponentData(frame, component), + scaleX: component.h / frame.maxH, + scaleY: component.v / frame.maxV, + blocksPerLine: component.blocksPerLine, + blocksPerColumn: component.blocksPerColumn, + }); + } + + console.log("components"); + console.log(this.components); + this.numComponents = this.components.length; + }, + + _getLinearizedBlockData: function getLinearizedBlockData(width, height) { + var scaleX = this.width / width, scaleY = this.height / height; + + var component, componentScaleX, componentScaleY, blocksPerScanline; + var x, y, i, j, k; + var index; + var offset = 0; + var output; + var numComponents = this.components.length; + var dataLength = width * height * numComponents; + var data = new Uint8Array(dataLength); + var xScaleBlockOffset = new Uint32Array(width); + var mask3LSB = 0xfffffff8; // used to clear the 3 LSBs + + for (i = 0; i < numComponents; i++) { + component = this.components[i]; + componentScaleX = component.scaleX * scaleX; + componentScaleY = component.scaleY * scaleY; + offset = i; + output = component.output; + blocksPerScanline = (component.blocksPerLine + 1) << 3; + // precalculate the xScaleBlockOffset + for (x = 0; x < width; x++) { + j = 0 | (x * componentScaleX); + xScaleBlockOffset[x] = ((j & mask3LSB) << 3) | (j & 7); + } + // linearize the blocks of the component + for (y = 0; y < height; y++) { + j = 0 | (y * componentScaleY); + index = blocksPerScanline * (j & mask3LSB) | ((j & 7) << 3); + for (x = 0; x < width; x++) { + data[offset] = output[index + xScaleBlockOffset[x]]; + offset += numComponents; + } + } + } + + // decodeTransform contains pairs of multiplier (-256..256) and additive + var transform = this.decodeTransform; + if (transform) { + for (i = 0; i < dataLength;) { + for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) { + data[i] = ((data[i] * transform[k]) >> 8) + transform[k + 1]; + } + } + } + return data; + }, + + _isColorConversionNeeded: function isColorConversionNeeded() { + if (this.adobe && this.adobe.transformCode) { + // The adobe transform marker overrides any previous setting + return true; + } else if (this.numComponents === 3) { + if (!this.adobe && this.colorTransform === 0) { + // If the Adobe transform marker is not present and the image + // dictionary has a 'ColorTransform' entry, explicitly set to `0`, + // then the colours should *not* be transformed. + return false; + } + return true; + } + // `this.numComponents !== 3` + if (!this.adobe && this.colorTransform === 1) { + // If the Adobe transform marker is not present and the image + // dictionary has a 'ColorTransform' entry, explicitly set to `1`, + // then the colours should be transformed. + return true; + } + return false; + }, + + _convertYccToRgb: function convertYccToRgb(data) { + var Y, Cb, Cr; + for (var i = 0, length = data.length; i < length; i += 3) { + Y = data[i ]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i ] = clamp0to255(Y - 179.456 + 1.402 * Cr); + data[i + 1] = clamp0to255(Y + 135.459 - 0.344 * Cb - 0.714 * Cr); + data[i + 2] = clamp0to255(Y - 226.816 + 1.772 * Cb); + } + return data; + }, + + _convertYcckToRgb: function convertYcckToRgb(data) { + var Y, Cb, Cr, k; + var offset = 0; + for (var i = 0, length = data.length; i < length; i += 4) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + k = data[i + 3]; + + var r = -122.67195406894 + + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - + 5.4080610064599e-5 * Y + 0.00048449797120281 * k - + 0.154362151871126) + + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - + 0.00477271405408747 * k + 1.53380253221734) + + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + + 0.48357088451265) + + k * (-0.000336197177618394 * k + 0.484791561490776); + + var g = 107.268039397724 + + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + + 0.000659397001245577 * Y + 0.000426105652938837 * k - + 0.176491792462875) + + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + + 0.000770482631801132 * k - 0.151051492775562) + + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + + 0.25802910206845) + + k * (-0.000318913117588328 * k - 0.213742400323665); + + var b = -20.810012546947 + + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + + 0.0020741088115012 * Y - 0.00288260236853442 * k + + 0.814272968359295) + + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + + 0.000560833691242812 * k - 0.195152027534049) + + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + + 0.116935020465145) + + k * (-0.000343531996510555 * k + 0.24165260232407); + + data[offset++] = clamp0to255(r); + data[offset++] = clamp0to255(g); + data[offset++] = clamp0to255(b); + } + return data; + }, + + _convertYcckToCmyk: function convertYcckToCmyk(data) { + var Y, Cb, Cr; + for (var i = 0, length = data.length; i < length; i += 4) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i ] = clamp0to255(434.456 - Y - 1.402 * Cr); + data[i + 1] = clamp0to255(119.541 - Y + 0.344 * Cb + 0.714 * Cr); + data[i + 2] = clamp0to255(481.816 - Y - 1.772 * Cb); + // K in data[i + 3] is unchanged + } + return data; + }, + + _convertCmykToRgb: function convertCmykToRgb(data) { + var c, m, y, k; + var offset = 0; + var min = -255 * 255 * 255; + var scale = 1 / 255 / 255; + for (var i = 0, length = data.length; i < length; i += 4) { + c = data[i]; + m = data[i + 1]; + y = data[i + 2]; + k = data[i + 3]; + + var r = + c * (-4.387332384609988 * c + 54.48615194189176 * m + + 18.82290502165302 * y + 212.25662451639585 * k - + 72734.4411664936) + + m * (1.7149763477362134 * m - 5.6096736904047315 * y - + 17.873870861415444 * k - 1401.7366389350734) + + y * (-2.5217340131683033 * y - 21.248923337353073 * k + + 4465.541406466231) - + k * (21.86122147463605 * k + 48317.86113160301); + var g = + c * (8.841041422036149 * c + 60.118027045597366 * m + + 6.871425592049007 * y + 31.159100130055922 * k - + 20220.756542821975) + + m * (-15.310361306967817 * m + 17.575251261109482 * y + + 131.35250912493976 * k - 48691.05921601825) + + y * (4.444339102852739 * y + 9.8632861493405 * k - + 6341.191035517494) - + k * (20.737325471181034 * k + 47890.15695978492); + var b = + c * (0.8842522430003296 * c + 8.078677503112928 * m + + 30.89978309703729 * y - 0.23883238689178934 * k - + 3616.812083916688) + + m * (10.49593273432072 * m + 63.02378494754052 * y + + 50.606957656360734 * k - 28620.90484698408) + + y * (0.03296041114873217 * y + 115.60384449646641 * k - + 49363.43385999684) - + k * (22.33816807309886 * k + 45932.16563550634); + + data[offset++] = r >= 0 ? 255 : r <= min ? 0 : 255 + r * scale | 0; + data[offset++] = g >= 0 ? 255 : g <= min ? 0 : 255 + g * scale | 0; + data[offset++] = b >= 0 ? 255 : b <= min ? 0 : 255 + b * scale | 0; + } + return data; + }, + + getData: function getData(width, height, forceRGBoutput) { + if (this.numComponents > 4) { + error('JPEG error: Unsupported color mode'); + } + // type of data: Uint8Array(width * height * numComponents) + var data = this._getLinearizedBlockData(width, height); + + if (this.numComponents === 1 && forceRGBoutput) { + var dataLength = data.length; + var rgbData = new Uint8Array(dataLength * 3); + var offset = 0; + for (var i = 0; i < dataLength; i++) { + var grayColor = data[i]; + rgbData[offset++] = grayColor; + rgbData[offset++] = grayColor; + rgbData[offset++] = grayColor; + } + return rgbData; + } else if (this.numComponents === 3 && this._isColorConversionNeeded()) { + return this._convertYccToRgb(data); + } else if (this.numComponents === 4) { + if (this._isColorConversionNeeded()) { + if (forceRGBoutput) { + return this._convertYcckToRgb(data); + } + return this._convertYcckToCmyk(data); + } else if (forceRGBoutput) { + return this._convertCmykToRgb(data); + } + } + return data; + }, + }; + + return JpegImage; +})(); + +// export { +// JpegImage, +// }; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 3c335441a..fc759fb56 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,35 +1,134 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.IO; using System.IO.Compression; using System.Text; -using ImageSharp.Formats; -using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + // TODO: Fix all bugs, and re enable Skipped and commented stuff !!! public class PngDecoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; - public static readonly string[] TestFiles = + public static readonly string[] CommonTestImages = + { + TestImages.Png.Splash, TestImages.Png.Indexed, + TestImages.Png.FilterVar, + TestImages.Png.Bad.ChunkLength1, + + TestImages.Png.VimImage1, + TestImages.Png.VersioningImage1, + TestImages.Png.VersioningImage2, + + TestImages.Png.SnakeGame, + TestImages.Png.Banner7Adam7InterlaceMode, + TestImages.Png.Banner8Index, + }; + + + public static readonly string[] TestImages48Bpp = { - TestImages.Png.Splash, TestImages.Png.Indexed, TestImages.Png.Interlaced, TestImages.Png.FilterVar, - TestImages.Png.Bad.ChunkLength1, TestImages.Png.Bad.ChunkLength2, TestImages.Png.Rgb48Bpp, TestImages.Png.Rgb48BppInterlaced + TestImages.Png.Rgb48Bpp, + TestImages.Png.Rgb48BppInterlaced }; + // This is a workaround for Mono-s decoder being incompatible with ours and GDI+. + // We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA! + public static readonly string[] WindowsOnlyTestImages = + { + TestImages.Png.Bad.ChunkLength2, + TestImages.Png.VimImage2, + }; + + [Theory] + [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] + public void Decode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + + // This is a workaround for Mono-s decoder being incompatible with ours and GDI+. + // We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA! + [Theory] + [WithFileCollection(nameof(WindowsOnlyTestImages), PixelTypes.Rgba32)] + public void Decode_WindowsOnlyTestImages(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + + if (!TestEnvironment.IsLinux) + { + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + } + + [Theory] + [WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)] + public void Decode_Interlaced_DoesNotThrow(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)] + public void Decode_Interlaced_ImageIsCorrect(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + + // TODO: We need to decode these into Rgba64 properly, and do 'CompareToOriginal' in a Rgba64 mode! (See #285) + [Theory] + [WithFileCollection(nameof(TestImages48Bpp), PixelTypes.Rgba32)] + public void Decode_48Bpp(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new PngDecoder())) + { + image.DebugSave(provider); + + // Workaround a bug in mono-s System.Drawing PNG decoder. It can't deal with 48Bpp png-s :( + if (!TestEnvironment.IsLinux) + { + image.CompareToOriginal(provider, ImageComparer.Exact); + } + } + } + [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes)] - public void DecodeAndReSave(TestImageProvider imageProvider) + [WithFile(TestImages.Png.Splash, PixelTypes)] + public void Decoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = imageProvider.GetImage()) + using (Image image = provider.GetImage(new PngDecoder())) { - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 24907cfdb..1566ddf44 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -using ImageSharp.Formats; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Threading.Tasks; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - - using Xunit; - public class PngEncoderTests : FileTestBase { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; @@ -31,10 +28,10 @@ namespace ImageSharp.Tests { using (Image image = provider.GetImage()) { - PngEncoder options = new PngEncoder() - { - PngColorType = pngColorType - }; + var options = new PngEncoder() + { + PngColorType = pngColorType + }; provider.Utility.TestName += "_" + pngColorType; provider.Utility.SaveTestOutputFile(image, "png", options); @@ -47,7 +44,7 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { using (Image image = provider.GetImage()) - using (MemoryStream ms = new MemoryStream()) + using (var ms = new MemoryStream()) { image.Save(ms, new PngEncoder()); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index c977f5fb5..fc17df93d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -1,22 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Formats.Png -{ - using System; - using System.Collections.Generic; - using System.Text; - using System.IO; - using Xunit; - using ImageSharp.Formats; - using System.Linq; - using ImageSharp.IO; - using System.Numerics; - - using ImageSharp.PixelFormats; +using System.IO; +using Xunit; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Formats.Png; +namespace SixLabors.ImageSharp.Tests.Formats.Png +{ public class PngSmokeTests { [Theory] @@ -34,8 +27,8 @@ namespace ImageSharp.Tests.Formats.Png ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { + ImageComparer.Tolerant().VerifySimilarity(image, img2); // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.CheckSimilarity(image, img2); } } } @@ -56,8 +49,7 @@ namespace ImageSharp.Tests.Formats.Png // ms.Position = 0; // using (Image img2 = Image.Load(ms, new PngDecoder())) // { - // // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - // ImageComparer.CheckSimilarity(image, img2, 0.03f); + // ImageComparer.VerifySimilarity(image, img2, 0.03f); // } // } //} @@ -115,14 +107,14 @@ namespace ImageSharp.Tests.Formats.Png using (MemoryStream ms = new MemoryStream()) { // image.Save(provider.Utility.GetTestOutputFileName("png")); - image.Resize(100, 100); + image.Mutate(x => x.Resize(100, 100)); // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); image.Save(ms, new PngEncoder()); ms.Position = 0; using (Image img2 = Image.Load(ms, new PngDecoder())) { - ImageComparer.CheckSimilarity(image, img2); + ImageComparer.Tolerant().VerifySimilarity(image, img2); } } } diff --git a/tests/ImageSharp.Tests/GlobalSuppressions.cs b/tests/ImageSharp.Tests/GlobalSuppressions.cs new file mode 100644 index 000000000..3d161049b --- /dev/null +++ b/tests/ImageSharp.Tests/GlobalSuppressions.cs @@ -0,0 +1,10 @@ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1013:Public method should be marked as test")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Assertions", "xUnit2013:Do not use equality check to check for collection size.")] + diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs index ba6d5687c..83075dc83 100644 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ b/tests/ImageSharp.Tests/Helpers/GuardTests.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Helpers -{ - using System; - using System.Diagnostics.CodeAnalysis; - - using Xunit; +using System; +using System.Diagnostics.CodeAnalysis; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Helpers +{ /// /// Tests the helper. /// diff --git a/tests/ImageSharp.Tests/Helpers/MathFTests.cs b/tests/ImageSharp.Tests/Helpers/MathFTests.cs index 62a971f9f..f865353c4 100644 --- a/tests/ImageSharp.Tests/Helpers/MathFTests.cs +++ b/tests/ImageSharp.Tests/Helpers/MathFTests.cs @@ -1,9 +1,11 @@ -namespace ImageSharp.Tests.Helpers -{ - using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Helpers +{ public class MathFTests { [Fact] diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs index 4cdf9122a..254cfa2dc 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.CopyBytesTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs index 06962e010..d8408523b 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.GetBytesTests.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using ImageSharp.IO; - using Xunit; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs index a69727d45..65963918c 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs b/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs index ffd9cdedc..87adead33 100644 --- a/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs +++ b/tests/ImageSharp.Tests/IO/EndianBinaryReaderTests.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using System.IO; - using System.Text; - - using ImageSharp.IO; - - using Xunit; +using System; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The endian binary reader tests. /// @@ -49,8 +45,7 @@ namespace ImageSharp.Tests.IO [Fact] public void ReadCharsBeyondProvidedBufferSize() { - Assert.Throws( - typeof(ArgumentException), + Assert.Throws( () => { MemoryStream stream = new MemoryStream(TestBytes); diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs index 5ff47409b..97d9275ad 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.CopyBytesTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs index 7fd7a97d4..eae8ca291 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.GetBytesTests.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using ImageSharp.IO; - using Xunit; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs index c46c011a1..f35836257 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using ImageSharp.IO; - using Xunit; +using System; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ /// /// The tests. /// diff --git a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs index 472d643cd..3fa94d671 100644 --- a/tests/ImageSharp.Tests/IO/LocalFileSystem.cs +++ b/tests/ImageSharp.Tests/IO/LocalFileSystem.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.IO -{ - using System; - using System.IO; - using ImageSharp.IO; - - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.IO; +using Xunit; +namespace SixLabors.ImageSharp.Tests.IO +{ public class LocalFileSystemTests { [Fact] diff --git a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs index d34fa22e2..3b6657993 100644 --- a/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs +++ b/tests/ImageSharp.Tests/Image/ImageDiscoverMimeType.cs @@ -1,19 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - using Moq; - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/Image/ImageEqualTests.cs b/tests/ImageSharp.Tests/Image/ImageEqualTests.cs deleted file mode 100644 index 399006012..000000000 --- a/tests/ImageSharp.Tests/Image/ImageEqualTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using Xunit; - - public class ImageEqualTests - { - [Fact] - public void TestsThatVimImagesAreEqual() - { - var image1Provider = TestImageProvider.File(TestImages.Png.VimImage1); - var image2Provider = TestImageProvider.File(TestImages.Png.VimImage2); - - using (Image img1 = image1Provider.GetImage()) - using (Image img2 = image2Provider.GetImage()) - { - bool imagesEqual = AreImagesEqual(img1, img2); - Assert.True(imagesEqual); - } - } - - [Fact] - public void TestsThatVersioningImagesAreEqual() - { - var image1Provider = TestImageProvider.File(TestImages.Png.VersioningImage1); - var image2Provider = TestImageProvider.File(TestImages.Png.VersioningImage2); - - using (Image img1 = image1Provider.GetImage()) - using (Image img2 = image2Provider.GetImage()) - { - bool imagesEqual = AreImagesEqual(img1, img2); - Assert.True(imagesEqual); - } - } - - private bool AreImagesEqual(Image img1, Image img2) - { - Assert.Equal(img1.Width, img2.Width); - Assert.Equal(img1.Height, img2.Height); - - for (int y = 0; y < img1.Height; y++) - { - for (int x = 0; x < img1.Width; x++) - { - Rgba32 pixel1 = img1[x, y]; - Rgba32 pixel2 = img2[x, y]; - - if (pixel1 != pixel2) - { - return false; - } - } - } - - return true; - } - } -} diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs new file mode 100644 index 000000000..e19d7ddef --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.Advanced; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class ImageFramesCollectionTests + { + [Fact] + public void ImageFramesaLwaysHaveOneFrame() + { + var collection = new ImageFrameCollection(10, 10); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void AddNewFrame_FramesMustHaveSameSize() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentException ex = Assert.Throws(() => + { + collection.Add(new ImageFrame(1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void AddNewFrame_FramesNotBeNull() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentNullException ex = Assert.Throws(() => + { + collection.Add(null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesMustHaveSameSize() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentException ex = Assert.Throws(() => + { + collection.Insert(1, new ImageFrame(1, 1)); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void InsertNewFrame_FramesNotBeNull() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentNullException ex = Assert.Throws(() => + { + collection.Insert(1, null); + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void SetFrameAtIndex_FramesMustHaveSameSize() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentException ex = Assert.Throws(() => + { + collection[0] = new ImageFrame(1, 1); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void SetFrameAtIndex_FramesNotBeNull() + { + var collection = new ImageFrameCollection(10, 10); + + ArgumentNullException ex = Assert.Throws(() => + { + collection[0] = null; + }); + + Assert.StartsWith("Value cannot be null.", ex.Message); + } + + [Fact] + public void Constructor_FramesMustHaveSameSize() + { + + ArgumentException ex = Assert.Throws(() => + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(1,1), + }); + }); + + Assert.StartsWith("Frame must have the same dimensions as the image.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_ThrowIfRemovingLastFrame() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10) + }); + + InvalidOperationException ex = Assert.Throws(() => + { + collection.RemoveAt(0); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveAtFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.RemoveAt(0); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void RemoveFrame_ThrowIfRemovingLastFrame() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10) + }); + + InvalidOperationException ex = Assert.Throws(() => + { + collection.Remove(collection[0]); + }); + Assert.Equal("Cannot remove last frame.", ex.Message); + } + + [Fact] + public void RemoveFrame_CanRemoveFrameZeroIfMultipleFramesExist() + { + + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.Remove(collection[0]); + Assert.Equal(1, collection.Count); + } + + [Fact] + public void RootFrameIsFrameAtIndexZero() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + Assert.Equal(collection.RootFrame, collection[0]); + } + + [Fact] + public void ConstructorPopulatesFrames() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + Assert.Equal(2, collection.Count); + } + + [Fact] + public void DisposeClearsCollection() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + collection.Dispose(); + + Assert.Equal(0, collection.Count); + } + + [Fact] + public void Dispose_DisposesAllInnerFrames() + { + var collection = new ImageFrameCollection(new[] { + new ImageFrame(10,10), + new ImageFrame(10,10), + }); + + IPixelSource[] framesSnapShot = collection.OfType>().ToArray(); + collection.Dispose(); + + Assert.All(framesSnapShot, f => + { + // the pixel source of the frame is null after its been disposed. + Assert.Null(f.PixelBuffer); + }); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index 4573d0d9d..f61c73636 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - - using ImageSharp.Formats; - using ImageSharp.IO; - using Moq; - using Xunit; +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using Moq; +using Xunit; +using SixLabors.ImageSharp.Advanced; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// @@ -320,6 +318,7 @@ namespace ImageSharp.Tests } } + [Fact] public void LoadsImageWithoutThrowingCrcException() { @@ -327,7 +326,7 @@ namespace ImageSharp.Tests using (Image img = image1Provider.GetImage()) { - Assert.Equal(166036, img.Pixels.Length); + Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length); } } diff --git a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs index 56cec4219..73c2f78f4 100644 --- a/tests/ImageSharp.Tests/Image/ImageRotationTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageRotationTests.cs @@ -1,7 +1,11 @@ -using SixLabors.Primitives; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Helpers; +using SixLabors.Primitives; using Xunit; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { public class ImageRotationTests { @@ -42,12 +46,12 @@ namespace ImageSharp.Tests private static (Size original, Size rotated) Rotate(int angle) { - TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = Image.Load(file.FilePath)) + var file = TestFile.Create(TestImages.Bmp.Car); + using (var image = Image.Load(file.FullPath)) { - Size original = image.Bounds.Size; - image.Rotate(angle); - return (original, image.Bounds.Size); + Size original = image.Size(); + image.Mutate(x => x.Rotate(angle)); + return (original, image.Size()); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index ad8a5cc7d..36d3b3c05 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - using System.Linq; - using ImageSharp.Formats; - using ImageSharp.IO; - using ImageSharp.PixelFormats; - - using Moq; - using Xunit; +using System; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using Moq; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// @@ -50,6 +47,93 @@ namespace ImageSharp.Tests this.Image = new Image(config, 1, 1); } + [Fact] + public void SavePixelData_Rgba32() + { + using (var img = new Image(2, 2)) + { + img[0, 0] = Rgba32.White; + img[1, 0] = Rgba32.Black; + + img[0, 1] = Rgba32.Red; + img[1, 1] = Rgba32.Blue; + var buffer = new byte[2 * 2 * 4]; // width * height * bytes per pixel + img.SavePixelData(buffer); + + Assert.Equal(255, buffer[0]); // 0, 0, R + Assert.Equal(255, buffer[1]); // 0, 0, G + Assert.Equal(255, buffer[2]); // 0, 0, B + Assert.Equal(255, buffer[3]); // 0, 0, A + + Assert.Equal(0, buffer[4]); // 1, 0, R + Assert.Equal(0, buffer[5]); // 1, 0, G + Assert.Equal(0, buffer[6]); // 1, 0, B + Assert.Equal(255, buffer[7]); // 1, 0, A + + Assert.Equal(255, buffer[8]); // 0, 1, R + Assert.Equal(0, buffer[9]); // 0, 1, G + Assert.Equal(0, buffer[10]); // 0, 1, B + Assert.Equal(255, buffer[11]); // 0, 1, A + + Assert.Equal(0, buffer[12]); // 1, 1, R + Assert.Equal(0, buffer[13]); // 1, 1, G + Assert.Equal(255, buffer[14]); // 1, 1, B + Assert.Equal(255, buffer[15]); // 1, 1, A + } + } + + + [Fact] + public void SavePixelData_Bgr24() + { + using (var img = new Image(2, 2)) + { + img[0, 0] = NamedColors.White; + img[1, 0] = NamedColors.Black; + + img[0, 1] = NamedColors.Red; + img[1, 1] = NamedColors.Blue; + + var buffer = new byte[2 * 2 * 3]; // width * height * bytes per pixel + img.SavePixelData(buffer); + + Assert.Equal(255, buffer[0]); // 0, 0, B + Assert.Equal(255, buffer[1]); // 0, 0, G + Assert.Equal(255, buffer[2]); // 0, 0, R + + Assert.Equal(0, buffer[3]); // 1, 0, B + Assert.Equal(0, buffer[4]); // 1, 0, G + Assert.Equal(0, buffer[5]); // 1, 0, R + + Assert.Equal(0, buffer[6]); // 0, 1, B + Assert.Equal(0, buffer[7]); // 0, 1, G + Assert.Equal(255, buffer[8]); // 0, 1, R + + Assert.Equal(255, buffer[9]); // 1, 1, B + Assert.Equal(0, buffer[10]); // 1, 1, G + Assert.Equal(0, buffer[11]); // 1, 1, R + } + } + + [Fact] + public void SavePixelData_Rgba32_Buffer_must_be_bigger() + { + using (var img = new Image(2, 2)) + { + img[0, 0] = Rgba32.White; + img[1, 0] = Rgba32.Black; + + img[0, 1] = Rgba32.Red; + img[1, 1] = Rgba32.Blue; + var buffer = new byte[2 * 2]; // width * height * bytes per pixel + + Assert.Throws(() => + { + img.SavePixelData(buffer); + }); + } + } + [Fact] public void SavePath() { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 217bf37fe..a9952f5c4 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// - public class ImageTests + public class ImageTests : FileTestBase { [Fact] public void ConstructorByteArray() @@ -37,7 +34,7 @@ namespace ImageSharp.Tests public void ConstructorFileSystem() { TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = Image.Load(file.FilePath)) + using (Image image = Image.Load(file.FullPath)) { Assert.Equal(600, image.Width); Assert.Equal(450, image.Height); @@ -67,8 +64,9 @@ namespace ImageSharp.Tests [Fact] public void Save_DetecedEncoding() { - string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.png"); - System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); + string file = System.IO.Path.Combine(dir, "Save_DetecedEncoding.png"); + using (Image image = new Image(10, 10)) { image.Save(file); @@ -81,9 +79,11 @@ namespace ImageSharp.Tests } [Fact] - public void Save_UnknownExtensionsEncoding() + public void Save_WhenExtensionIsUnknown_Throws() { - string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.tmp"); + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); + string file = System.IO.Path.Combine(dir, "Save_UnknownExtensionsEncoding_Throws.tmp"); + NotSupportedException ex = Assert.Throws( () => { @@ -97,8 +97,9 @@ namespace ImageSharp.Tests [Fact] public void Save_SetEncoding() { - string file = TestFile.GetPath("../../TestOutput/Save_SetEncoding.dat"); - System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + string dir = TestEnvironment.CreateOutputDirectory(nameof(ImageTests)); + string file = System.IO.Path.Combine(dir, "Save_SetEncoding.dat"); + using (Image image = new Image(10, 10)) { image.Save(file, new PngEncoder()); diff --git a/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs b/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs index bc36b60eb..10a531eaf 100644 --- a/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs +++ b/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs @@ -1,7 +1,10 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.IO; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal class NoneSeekableStream : Stream { diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index 0eeb468f0..1ab3f2ce9 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -1,36 +1,33 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// public class PixelAccessorTests { - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + var image = new Image(10, 10); using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { - Vector4 v = new Vector4(i, j, 0, 1); + var v = new Vector4(i, j, 0, 1); v /= 10; - TPixel color = default(TPixel); + var color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -45,7 +42,7 @@ namespace ImageSharp.Tests [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyx)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) + internal void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) where TPixel : struct, IPixel { using (Image src = provider.GetImage()) @@ -75,14 +72,14 @@ namespace ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) + internal void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) where TPixel : struct, IPixel { using (Image destImage = new Image(8, 8)) { using (Image srcImage = provider.GetImage()) { - srcImage.Fill(NamedColors.Red, new Rectangle(4, 4, 8, 8)); + srcImage.Mutate(x => x.Fill(NamedColors.Red, new Rectangle(4, 4, 8, 8))); using (PixelAccessor srcPixels = srcImage.Lock()) { using (PixelArea area = new PixelArea(8, 8, order)) @@ -100,8 +97,9 @@ namespace ImageSharp.Tests provider.Utility.SourceFileOrDescription = order.ToString(); provider.Utility.SaveTestOutputFile(destImage, "bmp"); - using (Image expectedImage = new Image(8, 8).Fill(NamedColors.Red)) + using (Image expectedImage = new Image(8, 8)) { + expectedImage.Mutate(x => x.Fill(NamedColors.Red)); Assert.True(destImage.IsEquivalentTo(expectedImage)); } } diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs deleted file mode 100644 index 6cd80e9e8..000000000 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ /dev/null @@ -1,160 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System; - using ImageSharp; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - /// - /// Class to perform simple image comparisons. - /// - public static class ImageComparer - { - const int DefaultScalingFactor = 32; // This is means the images get scaled into a 32x32 image to sample pixels - const int DefaultSegmentThreshold = 3; // The greyscale difference between 2 segements my be > 3 before it influences the overall difference - const float DefaultImageThreshold = 0.000F; // After segment thresholds the images must have no differences - - /// - /// Fills the bounded area with a solid color and does a visual comparison between 2 images asserting the difference outwith - /// that area is less then a configurable threshold. - /// - /// The color of the expected image - /// The color type fo the the actual image - /// The expected image - /// The actual image - /// The bounds within the image has been altered - /// - /// The threshold for the percentage difference where the images are asumed to be the same. - /// The default/undefined value is - /// - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// - public static void EnsureProcessorChangesAreConstrained(Image expected, Image actual, Rectangle bounds, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - // Draw identical shapes over the bounded and compare to ensure changes are constrained. - expected.Fill(NamedColors.HotPink, bounds); - actual.Fill(NamedColors.HotPink, bounds); - - CheckSimilarity(expected, actual, imageTheshold, segmentThreshold, scalingFactor); - } - - /// - /// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold - /// - /// The color of the expected image - /// The color type fo the the actual image - /// The expected image - /// The actual image - /// - /// The threshold for the percentage difference where the images are asumed to be the same. - /// The default/undefined value is - /// - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// - public static void CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - float percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); - - Assert.InRange(percentage, 0, imageTheshold); - } - - /// - /// Does a visual comparison between 2 images and then and returns the percentage diffence between the 2 - /// - /// The color of the source image - /// The color type for the target image - /// The source image - /// The target image - /// - /// The threshold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is - /// - /// - /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is - /// - /// Returns a number from 0 - 1 which represents the difference focter between the images. - public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - // code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET - Fast2DArray differences = GetDifferences(source, target, scalingFactor); - - int diffPixels = 0; - - foreach (byte b in differences.Data) - { - if (b > segmentThreshold) { diffPixels++; } - } - - return diffPixels / (float)(scalingFactor * scalingFactor); - } - - private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) - where TPixelA : struct, IPixel - where TPixelB : struct, IPixel - { - var differences = new Fast2DArray(scalingFactor, scalingFactor); - Fast2DArray firstGray = source.GetGrayScaleValues(scalingFactor); - Fast2DArray secondGray = target.GetGrayScaleValues(scalingFactor); - - for (int y = 0; y < scalingFactor; y++) - { - for (int x = 0; x < scalingFactor; x++) - { - int diff = firstGray[x, y] - secondGray[x, y]; - differences[x, y] = (byte)Math.Abs(diff); - } - } - - return differences; - } - - private static Fast2DArray GetGrayScaleValues(this Image source, int scalingFactor) - where TPixelA : struct, IPixel - { - byte[] buffer = new byte[3]; - using (Image img = new Image(source).Resize(scalingFactor, scalingFactor).Grayscale()) - { - using (PixelAccessor pixels = img.Lock()) - { - var grayScale = new Fast2DArray(scalingFactor, scalingFactor); - for (int y = 0; y < scalingFactor; y++) - { - for (int x = 0; x < scalingFactor; x++) - { - pixels[x, y].ToXyzBytes(buffer, 0); - grayScale[x, y] = buffer[0]; - } - } - - return grayScale; - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs new file mode 100644 index 000000000..59722a84d --- /dev/null +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -0,0 +1,104 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Moq; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + /// + /// Tests the configuration class. + /// + public class ImageOperationTests : IDisposable + { + private readonly Image image; + private readonly FakeImageOperationsProvider provider; + private readonly IImageProcessor processor; + + public Configuration Configuration { get; private set; } + + public ImageOperationTests() + { + this.provider = new FakeImageOperationsProvider(); + this.processor = new Mock>().Object; + this.image = new Image(new Configuration() + { + ImageOperationsProvider = this.provider + }, 1, 1); + } + + [Fact] + public void MutateCallsImageOperationsProvider_Func_OriginalImage() + { + this.image.Mutate(x => x.ApplyProcessor(this.processor)); + + Assert.True(this.provider.HasCreated(this.image)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x=>x.Processor)); + } + + [Fact] + public void MutateCallsImageOperationsProvider_ListOfProcessors_OriginalImage() + { + this.image.Mutate(this.processor); + + Assert.True(this.provider.HasCreated(this.image)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_Func_WithDuplicateImage() + { + var returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + + Assert.True(this.provider.HasCreated(returned)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_ListOfProcessors_WithDuplicateImage() + { + var returned = this.image.Clone(this.processor); + + Assert.True(this.provider.HasCreated(returned)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_Func_NotOnOrigional() + { + var returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + Assert.False(this.provider.HasCreated(this.image)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + } + + [Fact] + public void CloneCallsImageOperationsProvider_ListOfProcessors_NotOnOrigional() + { + var returned = this.image.Clone(this.processor); + Assert.False(this.provider.HasCreated(this.image)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + } + + [Fact] + public void ApplyProcessors_ListOfProcessors_AppliesAllProcessorsToOperation() + { + var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); + operations.ApplyProcessors(this.processor); + Assert.Contains(this.processor, operations.applied.Select(x => x.Processor)); + } + + public void Dispose() + { + this.image.Dispose(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index a070dbdb7..e8a6e8c59 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -5,17 +5,28 @@ full portable True + SixLabors.ImageSharp.Tests + SixLabors.ImageSharp.Tests + + + true + - - + + + + + @@ -27,4 +38,7 @@ PreserveNewest + + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs similarity index 71% rename from tests/ImageSharp.Tests/Common/Buffer2DTests.cs rename to tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 5f44a132d..d662a1b3e 100644 --- a/tests/ImageSharp.Tests/Common/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -1,16 +1,17 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Common +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Runtime.CompilerServices; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Tests.Common; using Xunit; - using static TestStructs; - - public unsafe class Buffer2DTests + public class Buffer2DTests { // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert @@ -30,7 +31,7 @@ namespace ImageSharp.Tests.Common [InlineData(1025, 17)] public void Construct(int width, int height) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -43,8 +44,8 @@ namespace ImageSharp.Tests.Common [InlineData(1025, 17)] public void Construct_FromExternalArray(int width, int height) { - Foo[] array = new Foo[width * height + 10]; - using (Buffer2D buffer = new Buffer2D(array, width, height)) + TestStructs.Foo[] array = new TestStructs.Foo[width * height + 10]; + using (Buffer2D buffer = new Buffer2D(array, width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -75,9 +76,9 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - Span span = buffer.GetRowSpan(y); + Span span = buffer.GetRowSpan(y); // Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); @@ -91,9 +92,9 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - Span span = buffer.GetRowSpan(x, y); + Span span = buffer.GetRowSpan(x, y); // Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); @@ -107,13 +108,13 @@ namespace ImageSharp.Tests.Common [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (Buffer2D buffer = new Buffer2D(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - Foo[] array = buffer.Array; + TestStructs.Foo[] array = buffer.Array; - ref Foo actual = ref buffer[x, y]; + ref TestStructs.Foo actual = ref buffer[x, y]; - ref Foo expected = ref array[y * width + x]; + ref TestStructs.Foo expected = ref array[y * width + x]; Assert.True(Unsafe.AreSame(ref expected, ref actual)); } diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs new file mode 100644 index 000000000..026b69498 --- /dev/null +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -0,0 +1,114 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Memory +{ + using System; + + using SixLabors.ImageSharp.Memory; + using SixLabors.Primitives; + + using Xunit; + + public class BufferAreaTests + { + [Fact] + public void Construct() + { + using (var buffer = new Buffer2D(10, 20)) + { + var rectangle = new Rectangle(3,2, 5, 6); + var area = new BufferArea(buffer, rectangle); + + Assert.Equal(buffer, area.DestinationBuffer); + Assert.Equal(rectangle, area.Rectangle); + } + } + + private static Buffer2D CreateTestBuffer(int w, int h) + { + var buffer = new Buffer2D(w, h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + buffer[x, y] = y * 100 + x; + } + } + return buffer; + } + + [Theory] + [InlineData(2, 3, 2, 2)] + [InlineData(5, 4, 3, 2)] + public void Indexer(int rx, int ry, int x, int y) + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + Rectangle r = new Rectangle(rx, ry, 5, 6); + + BufferArea area = buffer.GetArea(r); + + int value = area[x, y]; + int expected = (ry + y) * 100 + rx + x; + Assert.Equal(expected, value); + } + } + + [Theory] + [InlineData(2, 3, 2, 5, 6)] + [InlineData(5, 4, 3, 6, 5)] + public void GetRowSpan(int rx, int ry, int y, int w, int h) + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + Rectangle r = new Rectangle(rx, ry, w, h); + + BufferArea area = buffer.GetArea(r); + + Span span = area.GetRowSpan(y); + + Assert.Equal(w, span.Length); + + for (int i = 0; i < w; i++) + { + int expected = (ry + y) * 100 + rx + i; + int value = span[i]; + + Assert.Equal(expected, value); + } + } + } + + [Fact] + public void GetSubArea() + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + BufferArea area0 = buffer.GetArea(6, 8, 10, 10); + + BufferArea area1 = area0.GetSubArea(4, 4, 5, 5); + + var expectedRect = new Rectangle(10, 12, 5, 5); + + Assert.Equal(buffer, area1.DestinationBuffer); + Assert.Equal(expectedRect, area1.Rectangle); + + int value00 = 12 * 100 + 10; + Assert.Equal(value00, area1[0, 0]); + } + } + + [Fact] + public void DangerousGetPinnableReference() + { + using (Buffer2D buffer = CreateTestBuffer(20, 30)) + { + BufferArea area0 = buffer.GetArea(6, 8, 10, 10); + + ref int r = ref area0.GetReferenceToOrigo(); + + int expected = buffer[6, 8]; + Assert.Equal(expected, r); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/BufferTests.cs b/tests/ImageSharp.Tests/Memory/BufferTests.cs similarity index 69% rename from tests/ImageSharp.Tests/Common/BufferTests.cs rename to tests/ImageSharp.Tests/Memory/BufferTests.cs index 25ef173d4..e1efeb24e 100644 --- a/tests/ImageSharp.Tests/Common/BufferTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferTests.cs @@ -1,17 +1,15 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests.Common +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Threading.Tasks; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; using Xunit; - using static TestStructs; - public unsafe class BufferTests { // ReSharper disable once ClassNeverInstantiated.Local @@ -37,7 +35,7 @@ namespace ImageSharp.Tests.Common [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (Buffer buffer = new Buffer(count)) + using (Buffer buffer = new Buffer(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); @@ -51,8 +49,8 @@ namespace ImageSharp.Tests.Common [InlineData(1111)] public void ConstructWithExistingArray(int count) { - Foo[] array = new Foo[count]; - using (Buffer buffer = new Buffer(array)) + TestStructs.Foo[] array = new TestStructs.Foo[count]; + using (Buffer buffer = new Buffer(array)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); @@ -63,13 +61,13 @@ namespace ImageSharp.Tests.Common [Fact] public void Clear() { - Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; - using (Buffer buffer = new Buffer(a)) + TestStructs.Foo[] a = { new TestStructs.Foo() { A = 1, B = 2 }, new TestStructs.Foo() { A = 3, B = 4 } }; + using (Buffer buffer = new Buffer(a)) { buffer.Clear(); - Assert.Equal(default(Foo), a[0]); - Assert.Equal(default(Foo), a[1]); + Assert.Equal(default(TestStructs.Foo), a[0]); + Assert.Equal(default(TestStructs.Foo), a[1]); } } @@ -103,11 +101,11 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Read(int length, int index) { - Foo[] a = Foo.CreateArray(length); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - using (Buffer buffer = new Buffer(a)) + using (Buffer buffer = new Buffer(a)) { - Foo element = buffer[index]; + TestStructs.Foo element = buffer[index]; Assert.Equal(a[index], element); } @@ -117,13 +115,13 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Write(int length, int index) { - Foo[] a = Foo.CreateArray(length); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); - using (Buffer buffer = new Buffer(a)) + using (Buffer buffer = new Buffer(a)) { - buffer[index] = new Foo(666, 666); + buffer[index] = new TestStructs.Foo(666, 666); - Assert.Equal(new Foo(666, 666), a[index]); + Assert.Equal(new TestStructs.Foo(666, 666), a[index]); } } } @@ -131,7 +129,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Dispose() { - Buffer buffer = new Buffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -142,9 +140,9 @@ namespace ImageSharp.Tests.Common [InlineData(123)] public void CastToSpan(int bufferLength) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - Span span = buffer; + Span span = buffer; //Assert.Equal(buffer.Array, span.ToArray()); //Assert.Equal(0, span.Start); @@ -156,9 +154,9 @@ namespace ImageSharp.Tests.Common [Fact] public void Span() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = new Buffer(42)) { - Span span = buffer.Span; + Span span = buffer.Span; // Assert.Equal(buffer.Array, span.ToArray()); // Assert.Equal(0, span.Start); @@ -175,9 +173,9 @@ namespace ImageSharp.Tests.Common [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - Span span = buffer.Slice(start); + Span span = buffer.Slice(start); Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, bufferLength - start); @@ -189,9 +187,9 @@ namespace ImageSharp.Tests.Common [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (Buffer buffer = new Buffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - Span span = buffer.Slice(start, spanLength); + Span span = buffer.Slice(start, spanLength); Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, spanLength); @@ -202,8 +200,8 @@ namespace ImageSharp.Tests.Common [Fact] public void UnPinAndTakeArrayOwnership() { - Foo[] data = null; - using (Buffer buffer = new Buffer(42)) + TestStructs.Foo[] data = null; + using (Buffer buffer = new Buffer(42)) { data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); @@ -218,10 +216,10 @@ namespace ImageSharp.Tests.Common [Fact] public void ReturnsPinnedPointerToTheBeginningOfArray() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = new Buffer(42)) { - Foo* actual = (Foo*)buffer.Pin(); - fixed (Foo* expected = buffer.Array) + TestStructs.Foo* actual = (TestStructs.Foo*)buffer.Pin(); + fixed (TestStructs.Foo* expected = buffer.Array) { Assert.Equal(expected, actual); } @@ -231,7 +229,7 @@ namespace ImageSharp.Tests.Common [Fact] public void SecondCallReturnsTheSamePointer() { - using (Buffer buffer = new Buffer(42)) + using (Buffer buffer = new Buffer(42)) { IntPtr ptr1 = buffer.Pin(); IntPtr ptr2 = buffer.Pin(); @@ -243,7 +241,7 @@ namespace ImageSharp.Tests.Common [Fact] public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() { - Buffer buffer = new Buffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.Throws(() => buffer.Pin()); diff --git a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs similarity index 91% rename from tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs rename to tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs index efdcaa848..5cdbe638a 100644 --- a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs +++ b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Common +namespace SixLabors.ImageSharp.Tests.Memory { using System; - using ImageSharp.Memory; + using SixLabors.ImageSharp.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs b/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs similarity index 82% rename from tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs rename to tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs index 21e86d434..fdfd4c4b7 100644 --- a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs +++ b/tests/ImageSharp.Tests/Memory/PixelDataPoolTests.cs @@ -1,15 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + + // ReSharper disable InconsistentNaming -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Memory { - using System.Linq; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; using Xunit; diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs similarity index 63% rename from tests/ImageSharp.Tests/Common/BufferSpanTests.cs rename to tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs index af33a981b..395c32546 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/tests/ImageSharp.Tests/Memory/SpanUtilityTests.cs @@ -1,20 +1,18 @@ -// ReSharper disable ObjectCreationAsStatement -// ReSharper disable InconsistentNaming +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. -namespace ImageSharp.Tests.Common +namespace SixLabors.ImageSharp.Tests.Memory { using System; using System.Numerics; using System.Runtime.CompilerServices; - using ImageSharp.Memory; - using ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.Tests.Common; using Xunit; - using static TestStructs; - - public unsafe class SpanTests + public unsafe class SpanUtilityTests { // ReSharper disable once ClassNeverInstantiated.Local private class Assert : Xunit.Assert @@ -23,7 +21,7 @@ namespace ImageSharp.Tests.Common { ref T1 bb = ref Unsafe.As(ref b); - True(Unsafe.AreSame(ref a, ref bb), "References are not same!"); + Assert.True(Unsafe.AreSame(ref a, ref bb), "References are not same!"); } } @@ -45,15 +43,15 @@ namespace ImageSharp.Tests.Common [Fact] public void AsBytes() { - Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) }; + TestStructs.Foo[] fooz = { new TestStructs.Foo(1, 2), new TestStructs.Foo(3, 4), new TestStructs.Foo(5, 6) }; - using (Buffer colorBuf = new Buffer(fooz)) + using (Buffer colorBuf = new Buffer(fooz)) { - Span orig = colorBuf.Slice(1); + Span orig = colorBuf.Slice(1); Span asBytes = orig.AsBytes(); // Assert.Equal(asBytes.Start, sizeof(Foo)); - Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); + Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference()); } } @@ -63,10 +61,10 @@ namespace ImageSharp.Tests.Common [Fact] public void Basic() { - Foo[] array = Foo.CreateArray(3); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(3); // Act: - Span span = new Span(array); + Span span = new Span(array); // Assert: Assert.Equal(array, span.ToArray()); @@ -77,11 +75,11 @@ namespace ImageSharp.Tests.Common [Fact] public void WithStart() { - Foo[] array = Foo.CreateArray(4); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(4); int start = 2; // Act: - Span span = new Span(array, start); + Span span = new Span(array, start); // Assert: Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); @@ -91,11 +89,11 @@ namespace ImageSharp.Tests.Common [Fact] public void WithStartAndLength() { - Foo[] array = Foo.CreateArray(10); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(10); int start = 2; int length = 3; // Act: - Span span = new Span(array, start, length); + Span span = new Span(array, start, length); // Assert: Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); @@ -108,12 +106,12 @@ namespace ImageSharp.Tests.Common [Fact] public void StartOnly() { - Foo[] array = Foo.CreateArray(5); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(5); int start0 = 2; int start1 = 2; int totalOffset = start0 + start1; - Span span = new Span(array, start0); + Span span = new Span(array, start0); // Act: span = span.Slice(start1); @@ -126,13 +124,13 @@ namespace ImageSharp.Tests.Common [Fact] public void StartAndLength() { - Foo[] array = Foo.CreateArray(10); + TestStructs.Foo[] array = TestStructs.Foo.CreateArray(10); int start0 = 2; int start1 = 2; int totalOffset = start0 + start1; int sliceLength = 3; - Span span = new Span(array, start0); + Span span = new Span(array, start0); // Act: span = span.Slice(start1, sliceLength); @@ -179,10 +177,10 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Read(int length, int start, int index) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); - Foo element = span[index]; + TestStructs.Foo element = span[index]; Assert.Equal(a[start + index], element); } @@ -191,12 +189,12 @@ namespace ImageSharp.Tests.Common [MemberData(nameof(IndexerData))] public void Write(int length, int start, int index) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); - span[index] = new Foo(666, 666); + span[index] = new TestStructs.Foo(666, 666); - Assert.Equal(new Foo(666, 666), a[start + index]); + Assert.Equal(new TestStructs.Foo(666, 666), a[start + index]); } [Theory] @@ -206,15 +204,15 @@ namespace ImageSharp.Tests.Common [InlineData(10, 1, 1, 7)] public void AsBytes_Read(int length, int start, int index, int byteOffset) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); Span bytes = span.AsBytes(); - byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; + byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; - ref byte baseRef = ref Unsafe.As(ref a[0]); - byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); + ref byte baseRef = ref Unsafe.As(ref a[0]); + byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); Assert.Equal(expected, actual); } @@ -226,9 +224,9 @@ namespace ImageSharp.Tests.Common [InlineData(3, 4)] public void DangerousGetPinnableReference(int start, int length) { - Foo[] a = Foo.CreateArray(length); - Span span = new Span(a, start); - ref Foo r = ref span.DangerousGetPinnableReference(); + TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); + Span span = new Span(a, start); + ref TestStructs.Foo r = ref span.DangerousGetPinnableReference(); Assert.True(Unsafe.AreSame(ref a[start], ref r)); } @@ -266,11 +264,11 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToOwnType(int count) { - Foo[] source = Foo.CreateArray(count + 2); - Foo[] dest = new Foo[count + 5]; + TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); + TestStructs.Foo[] dest = new TestStructs.Foo[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -289,11 +287,11 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToOwnType_Aligned(int count) { - AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); - AlignedFoo[] dest = new AlignedFoo[count + 5]; + TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); + TestStructs.AlignedFoo[] dest = new TestStructs.AlignedFoo[count + 5]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); SpanHelper.Copy(apSource, apDest, count - 1); @@ -335,22 +333,22 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToBytes(int count) { - int destCount = count * sizeof(Foo); - Foo[] source = Foo.CreateArray(count + 2); - byte[] dest = new byte[destCount + sizeof(Foo) * 2]; + int destCount = count * sizeof(TestStructs.Foo); + TestStructs.Foo[] source = TestStructs.Foo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(TestStructs.Foo) * 2]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, sizeof(Foo)); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, sizeof(TestStructs.Foo)); - SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(Foo)); + SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.Foo)); AssertNotDefault(source, 1); - Assert.False(ElementsAreEqual(source, dest, 0)); - Assert.True(ElementsAreEqual(source, dest, 1)); - Assert.True(ElementsAreEqual(source, dest, 2)); - Assert.True(ElementsAreEqual(source, dest, count - 1)); - Assert.False(ElementsAreEqual(source, dest, count)); + Assert.False((bool)ElementsAreEqual(source, dest, 0)); + Assert.True((bool)ElementsAreEqual(source, dest, 1)); + Assert.True((bool)ElementsAreEqual(source, dest, 2)); + Assert.True((bool)ElementsAreEqual(source, dest, count - 1)); + Assert.False((bool)ElementsAreEqual(source, dest, count)); } [Theory] @@ -358,22 +356,22 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void GenericToBytes_Aligned(int count) { - int destCount = count * sizeof(Foo); - AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); - byte[] dest = new byte[destCount + sizeof(AlignedFoo) * 2]; + int destCount = count * sizeof(TestStructs.Foo); + TestStructs.AlignedFoo[] source = TestStructs.AlignedFoo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(TestStructs.AlignedFoo) * 2]; - Span apSource = new Span(source, 1); - Span apDest = new Span(dest, sizeof(AlignedFoo)); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, sizeof(TestStructs.AlignedFoo)); - SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(AlignedFoo)); + SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(TestStructs.AlignedFoo)); AssertNotDefault(source, 1); - Assert.False(ElementsAreEqual(source, dest, 0)); - Assert.True(ElementsAreEqual(source, dest, 1)); - Assert.True(ElementsAreEqual(source, dest, 2)); - Assert.True(ElementsAreEqual(source, dest, count - 1)); - Assert.False(ElementsAreEqual(source, dest, count)); + Assert.False((bool)ElementsAreEqual(source, dest, 0)); + Assert.True((bool)ElementsAreEqual(source, dest, 1)); + Assert.True((bool)ElementsAreEqual(source, dest, 2)); + Assert.True((bool)ElementsAreEqual(source, dest, count - 1)); + Assert.False((bool)ElementsAreEqual(source, dest, count)); } [Theory] @@ -392,9 +390,9 @@ namespace ImageSharp.Tests.Common AssertNotDefault(source, 1); - Assert.True(ElementsAreEqual(source, dest, 0)); - Assert.True(ElementsAreEqual(source, dest, count - 1)); - Assert.False(ElementsAreEqual(source, dest, count)); + Assert.True((bool)ElementsAreEqual(source, dest, 0)); + Assert.True((bool)ElementsAreEqual(source, dest, count - 1)); + Assert.False((bool)ElementsAreEqual(source, dest, count)); } [Theory] @@ -402,22 +400,22 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void BytesToGeneric(int count) { - int srcCount = count * sizeof(Foo); + int srcCount = count * sizeof(TestStructs.Foo); byte[] source = CreateTestBytes(srcCount); - Foo[] dest = new Foo[count + 2]; + TestStructs.Foo[] dest = new TestStructs.Foo[count + 2]; Span apSource = new Span(source); - Span apDest = new Span(dest); + Span apDest = new Span(dest); - SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(Foo)); + SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(TestStructs.Foo)); - AssertNotDefault(source, sizeof(Foo) + 1); + AssertNotDefault(source, sizeof(TestStructs.Foo) + 1); AssertNotDefault(dest, 1); - Assert.True(ElementsAreEqual(dest, source, 0)); - Assert.True(ElementsAreEqual(dest, source, 1)); - Assert.True(ElementsAreEqual(dest, source, count - 1)); - Assert.False(ElementsAreEqual(dest, source, count)); + Assert.True((bool)ElementsAreEqual(dest, source, 0)); + Assert.True((bool)ElementsAreEqual(dest, source, 1)); + Assert.True((bool)ElementsAreEqual(dest, source, count - 1)); + Assert.False((bool)ElementsAreEqual(dest, source, count)); } [Fact] @@ -439,29 +437,29 @@ namespace ImageSharp.Tests.Common } } - internal static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) + internal static bool ElementsAreEqual(TestStructs.Foo[] array, byte[] rawArray, int index) { - fixed (Foo* pArray = array) + fixed (TestStructs.Foo* pArray = array) fixed (byte* pRaw = rawArray) { - Foo* pCasted = (Foo*)pRaw; + TestStructs.Foo* pCasted = (TestStructs.Foo*)pRaw; - Foo val1 = pArray[index]; - Foo val2 = pCasted[index]; + TestStructs.Foo val1 = pArray[index]; + TestStructs.Foo val2 = pCasted[index]; return val1.Equals(val2); } } - internal static bool ElementsAreEqual(AlignedFoo[] array, byte[] rawArray, int index) + internal static bool ElementsAreEqual(TestStructs.AlignedFoo[] array, byte[] rawArray, int index) { - fixed (AlignedFoo* pArray = array) + fixed (TestStructs.AlignedFoo* pArray = array) fixed (byte* pRaw = rawArray) { - AlignedFoo* pCasted = (AlignedFoo*)pRaw; + TestStructs.AlignedFoo* pCasted = (TestStructs.AlignedFoo*)pRaw; - AlignedFoo val1 = pArray[index]; - AlignedFoo val2 = pCasted[index]; + TestStructs.AlignedFoo val1 = pArray[index]; + TestStructs.AlignedFoo val2 = pCasted[index]; return val1.Equals(val2); } diff --git a/tests/ImageSharp.Tests/Common/TestStructs.cs b/tests/ImageSharp.Tests/Memory/TestStructs.cs similarity index 91% rename from tests/ImageSharp.Tests/Common/TestStructs.cs rename to tests/ImageSharp.Tests/Memory/TestStructs.cs index f7f09bea2..608e3c6cb 100644 --- a/tests/ImageSharp.Tests/Common/TestStructs.cs +++ b/tests/ImageSharp.Tests/Memory/TestStructs.cs @@ -1,4 +1,7 @@ -namespace ImageSharp.Tests.Common +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Memory { using Xunit; diff --git a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs index da2bb4156..507401398 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs @@ -1,13 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.Formats; - using Xunit; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index c60c8978c..ee7faeca5 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -1,15 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs index 3b224014d..5e15d556c 100644 --- a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the class. /// diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index db22300fa..edeeebd28 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -1,20 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Text; +using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections; - using System.IO; - using System.Linq; - using System.Text; - - using ImageSharp.PixelFormats; - - using Xunit; - public class ExifProfileTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs index dc62f1cbf..dee6d5ff3 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Collections.ObjectModel; - using Xunit; +using System.Collections.ObjectModel; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ExifReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 1d36de5ef..2b8d4d716 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ExifDescriptionAttributeTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index 473af7712..8bc192af5 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Linq; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Linq; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ExifValueTests { private static ExifValue GetExifValue() diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs index 2bde12543..beca4db49 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.CurvesTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderCurvesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs index 52c67ba53..43a1ed7f4 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.LutTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderLutTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs index 9d148ec94..328cc3fa6 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MatrixTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderMatrixTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs index c02ef40e3..5599e80d1 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.MultiProcessElementTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderMultiProcessElementTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs index e3593bfa9..880fa0607 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.NonPrimitivesTests.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using System.Numerics; - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderNonPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs index b9b0c6655..f8924c43c 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.PrimitivesTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs index 76bb1cda1..aba587846 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderTagDataEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs index 635ce6168..a3e5a20f6 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReaderTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs index c04401f6d..6a47c988c 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.CurvesTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterCurvesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs index 4fcc55d01..9286ac815 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.LutTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterLutTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs index 61b5d57ff..437c223fa 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MatrixTests.cs @@ -1,16 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System.Numerics; - - using ImageSharp.Memory; - - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterMatrixTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs index e3bc37574..43165c617 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.MultiProcessElementTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterMultiProcessElementTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs index ae8345805..eda6a33c7 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.NonPrimitivesTests.cs @@ -1,14 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using System.Numerics; - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterNonPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs index 8d0cd32ab..56a4f8c0c 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using System; - using Xunit; +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterPrimitivesTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs index ea85bd16d..f3ef5effb 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterTagDataEntryTests { [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs index 5239d7cd5..a3f796275 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriterTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccDataWriterTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs index 34aa24fa6..b3215ee7a 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccReaderTests { [Fact] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs index 7e3f8c0c9..99ae73f49 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccWriterTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Icc -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Icc +{ public class IccWriterTests { [Fact] diff --git a/tests/ImageSharp.Tests/Numerics/RationalTests.cs b/tests/ImageSharp.Tests/Numerics/RationalTests.cs index 3d80b88fe..61eeed01f 100644 --- a/tests/ImageSharp.Tests/Numerics/RationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RationalTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs index cb7e21db0..af5388d1c 100644 --- a/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/SignedRationalTests.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using Xunit; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 76001ed3a..ca59cea72 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -1,12 +1,12 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class Bgr24Tests { public static readonly TheoryData ColorData = diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 1928d51f6..e3cf86825 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -1,12 +1,12 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class Bgra32Tests { public static readonly TheoryData ColorData = diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs index eac0644d9..b0d5929f4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System.Collections.Generic; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ public class ColorConstructorTests { public static IEnumerable Vector4Data diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs index db5b4a4c9..af4181cde 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class ColorDefinitionTests { public static TheoryData ColorNames diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs index 2967e7c86..d3815f2eb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ /// /// Test implementations of IEquatable /// diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs index 563b6be3c..ad8297fbb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System.Collections.Generic; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ public class ColorPackingTests { public static IEnumerable Vector4PackData diff --git a/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs index 773bfa513..303905baf 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs @@ -1,18 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Colors -{ - using System; - using System.Diagnostics; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Diagnostics; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ /// /// The packed pixel tests. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs index 97d550592..9aa2e01a6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.PixelFormats.PixelBlenders -{ - using System; - using System.Collections.Generic; - using System.Numerics; - using System.Text; - using ImageSharp.PixelFormats.PixelBlenders; - using ImageSharp.Tests.TestUtilities; - using Xunit; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ public class PorterDuffFunctionsTests { public static TheoryData NormalBlendFunctionData = new TheoryData() { diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index b2a663d07..b95f8fdf6 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.PixelFormats.PixelBlenders -{ - using System; - using System.Collections.Generic; - using System.Numerics; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.PixelFormats.PixelBlenders; - using ImageSharp.Tests.TestUtilities; - using Xunit; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders +{ public class PorterDuffFunctionsTests_TPixel { private static Span AsSpan(T value) diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs index 6bcada0ff..524747afe 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.PixelFormats -{ - using System; - using System.Collections.Generic; - using System.Text; - using ImageSharp.PixelFormats; - using ImageSharp.PixelFormats.PixelBlenders; - using ImageSharp.Tests.TestUtilities; - using Xunit; +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.PixelBlenders; +using SixLabors.ImageSharp.Tests.TestUtilities; +using Xunit; - public partial class PixelOperationsTests +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public class PixelBlenderTests { diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index 0a121cfce..dbb9a6c24 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -1,22 +1,20 @@ -// ReSharper disable InconsistentNaming -// ReSharper disable AccessToDisposedClosure -namespace ImageSharp.Tests.PixelFormats -{ - using System; - using System.Numerics; - - using ImageSharp.Memory; - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; - using Xunit.Abstractions; +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ public partial class PixelOperationsTests { - - public class Color32 : PixelOperationsTests + public class Rgba32 : PixelOperationsTests { - public Color32(ITestOutputHelper output) + public Rgba32(ITestOutputHelper output) : base(output) { } @@ -27,19 +25,19 @@ namespace ImageSharp.Tests.PixelFormats [Fact] public void IsSpecialImplementation() { - Assert.IsType(PixelOperations.Instance); + Assert.IsType(PixelOperations.Instance); } [Fact] public void ToVector4SimdAligned() { - Rgba32[] source = CreatePixelTestData(64); + ImageSharp.Rgba32[] source = CreatePixelTestData(64); Vector4[] expected = CreateExpectedVector4Data(source); TestOperation( source, expected, - (s, d) => Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) + (s, d) => ImageSharp.Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) ); } @@ -52,23 +50,23 @@ namespace ImageSharp.Tests.PixelFormats int times = 200000; int count = 1024; - using (Buffer source = new Buffer(count)) + using (Buffer source = new Buffer(count)) using (Buffer dest = new Buffer(count)) { this.Measure( times, () => { - PixelOperations.Instance.ToVector4(source, dest, count); + PixelOperations.Instance.ToVector4(source, dest, count); }); } } } - public class Argb : PixelOperationsTests + public class Argb32 : PixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public Argb(ITestOutputHelper output) + public Argb32(ITestOutputHelper output) : base(output) { } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index 1d0d024fb..4e85fe7e3 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -1,13 +1,13 @@ -// ReSharper disable InconsistentNaming -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ public class Rgb24Tests { public static readonly TheoryData ColorData = diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 5509cbddc..a8d38b938 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Numerics; - - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index 1f5df6d88..1dd280bee 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -1,17 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - using ImageSharp.PixelFormats; - - using Xunit; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// Tests the struct. /// diff --git a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs index 25a61453b..b5d46ba76 100644 --- a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs @@ -1,11 +1,12 @@ -namespace ImageSharp.Tests.Colors -{ - using System.Numerics; - - using ImageSharp.PixelFormats; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. - using Xunit; +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +namespace SixLabors.ImageSharp.Tests.Colors +{ public class UnPackedPixelTests { [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs index 642df598a..221b4a9bf 100644 --- a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs @@ -1,51 +1,30 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Binarization -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BinaryThresholdTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class BinaryThresholdTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData BinaryThresholdValues - = new TheoryData - { - .25F, - .75F - }; - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] - public void ImageShouldApplyBinaryThresholdFilter(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [Fact] + public void BinaryThreshold_CorrectProcessor() { - using (Image image = provider.GetImage()) - { - image.BinaryThreshold(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.BinaryThreshold(.23f); + var p = this.Verify>(); + Assert.Equal(.23f, p.Threshold); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] - public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [Fact] + public void BinaryThreshold_rect_CorrectProcessor() { - - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.BinaryThreshold(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.BinaryThreshold(.93f, this.rect); + var p = this.Verify>(this.rect); + Assert.Equal(.93f, p.Threshold); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs deleted file mode 100644 index a06397c86..000000000 --- a/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Processing.Binarization -{ - using ImageSharp.Dithering; - using ImageSharp.Dithering.Ordered; - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; - - public class DitherTest : FileTestBase - { - public static readonly TheoryData Ditherers = new TheoryData - { - { "Ordered", new Ordered() }, - { "Bayer", new Bayer() } - }; - - public static readonly TheoryData ErrorDiffusers = new TheoryData - { - { "Atkinson", new Atkinson() }, - { "Burks", new Burks() }, - { "FloydSteinberg", new FloydSteinberg() }, - { "JarvisJudiceNinke", new JarvisJudiceNinke() }, - { "Sierra2", new Sierra2() }, - { "Sierra3", new Sierra3() }, - { "SierraLite", new SierraLite() }, - { "Stucki", new Stucki() }, - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(Ditherers), DefaultPixelType)] - public void ImageShouldApplyDitherFilter(TestImageProvider provider, string name, IOrderedDither ditherer) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Dither(ditherer) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(Ditherers), DefaultPixelType)] - public void ImageShouldApplyDitherFilterInBox(TestImageProvider provider, string name, IOrderedDither ditherer) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Dither(ditherer, bounds) - .DebugSave(provider, name, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ErrorDiffusers), DefaultPixelType)] - public void ImageShouldApplyDiffusionFilter(TestImageProvider provider, string name, IErrorDiffuser diffuser) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Dither(diffuser, .5F) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ErrorDiffusers), DefaultPixelType)] - public void ImageShouldApplyDiffusionFilterInBox(TestImageProvider provider, string name, IErrorDiffuser diffuser) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Dither(diffuser,.5F, bounds) - .DebugSave(provider, name, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs new file mode 100644 index 000000000..94241d007 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Moq; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Binarization +{ + public class DitherTest : BaseImageOperationsExtensionTest + { + private readonly IOrderedDither orderedDither; + private readonly IErrorDiffuser errorDiffuser; + + public DitherTest() + { + this.orderedDither = new Mock().Object; + this.errorDiffuser = new Mock().Object; + } + [Fact] + public void Dither_CorrectProcessor() + { + this.operations.Dither(orderedDither); + var p = this.Verify>(); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(0, p.Index); + } + + [Fact] + public void Dither_rect_CorrectProcessor() + { + this.operations.Dither(orderedDither, this.rect); + var p = this.Verify>(this.rect); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(0, p.Index); + } + [Fact] + public void Dither_index_CorrectProcessor() + { + this.operations.Dither(orderedDither, 2); + var p = this.Verify>(); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(2, p.Index); + } + + [Fact] + public void Dither_index_rect_CorrectProcessor() + { + this.operations.Dither(orderedDither, this.rect, 2); + var p = this.Verify>(this.rect); + Assert.Equal(this.orderedDither, p.Dither); + Assert.Equal(2, p.Index); + } + + + [Fact] + public void Dither_ErrorDifuser_CorrectProcessor() + { + this.operations.Dither(errorDiffuser, 4); + var p = this.Verify>(); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(4, p.Threshold); + } + + [Fact] + public void Dither_ErrorDifuser_rect_CorrectProcessor() + { + this.operations.Dither(this.errorDiffuser, 3, this.rect); + var p = this.Verify>(this.rect); + Assert.Equal(this.errorDiffuser, p.Diffuser); + Assert.Equal(3, p.Threshold); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs index 4dc70fb0f..f6efdc9a9 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs @@ -1,43 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BlackWhiteTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class BlackWhiteTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBlackWhiteFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void BlackWhite_CorrectProcessor() { - using (Image image = provider.GetImage()) - { - image.BlackWhite() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.BlackWhite(); + var p = this.Verify>(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBlackWhiteFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void BlackWhite_rect_CorrectProcessor() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.BlackWhite(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.BlackWhite( this.rect); + var p = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs index bc1c8ad22..cc80e32d5 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs @@ -1,57 +1,44 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; - public class ColorBlindnessTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class ColorBlindnessTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData ColorBlindnessFilters - = new TheoryData - { - ColorBlindness.Achromatomaly, - ColorBlindness.Achromatopsia, - ColorBlindness.Deuteranomaly, - ColorBlindness.Deuteranopia, - ColorBlindness.Protanomaly, - ColorBlindness.Protanopia, - ColorBlindness.Tritanomaly, - ColorBlindness.Tritanopia + public static IEnumerable TheoryData = new[] { + new object[]{ new TestType>(), ColorBlindness.Achromatomaly }, + new object[]{ new TestType>(), ColorBlindness.Achromatopsia }, + new object[]{ new TestType>(), ColorBlindness.Deuteranomaly }, + new object[]{ new TestType>(), ColorBlindness.Deuteranopia }, + new object[]{ new TestType>(), ColorBlindness.Protanomaly }, + new object[]{ new TestType>(), ColorBlindness.Protanopia }, + new object[]{ new TestType>(), ColorBlindness.Tritanomaly }, + new object[]{ new TestType>(), ColorBlindness.Tritanopia } }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] - public void ImageShouldApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindness colorBlindness) - where TPixel : struct, IPixel + [MemberData(nameof(TheoryData))] + public void ColorBlindness_CorrectProcessor(TestType testType, ColorBlindness colorBlindness) + where T : IImageProcessor { - using (Image image = provider.GetImage()) - { - image.ColorBlindness(colorBlindness) - .DebugSave(provider, colorBlindness.ToString(), Extensions.Bmp); - } + this.operations.ColorBlindness(colorBlindness); + var p = this.Verify(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] - public void ImageShouldApplyColorBlindnessFilterInBox(TestImageProvider provider, ColorBlindness colorBlindness) - where TPixel : struct, IPixel + [MemberData(nameof(TheoryData))] + public void ColorBlindness_rect_CorrectProcessor(TestType testType, ColorBlindness colorBlindness) + where T : IImageProcessor { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.ColorBlindness(colorBlindness, bounds) - .DebugSave(provider, colorBlindness.ToString(), Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.ColorBlindness(colorBlindness, this.rect); + var p = this.Verify(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs index 9bf55a6d2..14697c623 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs @@ -1,61 +1,47 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; +using System.Collections; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; - public class GrayscaleTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class GrayscaleTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData GrayscaleModeTypes - = new TheoryData - { - GrayscaleMode.Bt601, - GrayscaleMode.Bt709 - }; + public static IEnumerable ModeTheoryData = new[] { + new object[]{ new TestType>(), GrayscaleMode.Bt709 } + }; - /// - /// Use test patterns over loaded images to save decode time. - /// [Theory] - [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] - public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) - where TPixel : struct, IPixel + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor { - using (Image image = provider.GetImage()) - { - image.Grayscale(value); - byte[] data = new byte[3]; - for (int i = 0; i < image.Pixels.Length; i++) - { - image.Pixels[i].ToXyzBytes(data, 0); - Assert.Equal(data[0], data[1]); - Assert.Equal(data[1], data[2]); - } + this.operations.Grayscale(mode); + var p = this.Verify(); - image.DebugSave(provider, value.ToString()); - } } [Theory] - [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] - public void ImageShouldApplyGrayscaleFilterInBox(TestImageProvider provider, GrayscaleMode value) - where TPixel : struct, IPixel + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_rect_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - image.Grayscale(value, bounds) - .DebugSave(provider, value.ToString()); + this.operations.Grayscale(mode, this.rect); + this.Verify(this.rect); + } - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + [Fact] + public void Grayscale_rect_CorrectProcessor() + { + this.operations.Grayscale(this.rect); + this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs index ba1cf2b8f..0ec03dfdd 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs @@ -1,50 +1,31 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class HueTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class HueTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData HueValues - = new TheoryData + [Fact] + public void Hue_amount_HueProcessorDefaultsSet() { - 180, - -180 - }; + this.operations.Hue(34f); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] - public void ImageShouldApplyHueFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Hue(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(34f, processor.Angle); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] - public void ImageShouldApplyHueFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Hue_amount_rect_HueProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Hue(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + this.operations.Hue(5f, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(5f, processor.Angle); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs index 971cdb6d8..61a4124c9 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs @@ -1,43 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class KodachromeTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class KodachromeTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyKodachromeFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Kodachrome_amount_KodachromeProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Kodachrome() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Kodachrome(); + var processor = this.Verify>(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyKodachromeFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Kodachrome_amount_rect_KodachromeProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Kodachrome(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.Kodachrome(this.rect); + var processor = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs index 5b41cdb3b..32107cb71 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs @@ -1,45 +1,28 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class LomographTest : FileTestBase +namespace SixLabors.ImageSharp.Tests +{ + public class LomographTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyLomographFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Lomograph_amount_LomographProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Lomograph() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Lomograph(); + var processor = this.Verify>(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyLomographFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Lomograph_amount_rect_LomographProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Lomograph(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.Lomograph(this.rect); + var processor = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs index 48f7a6c0a..10433aa9b 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs @@ -1,43 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class PolaroidTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class PolaroidTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyPolaroidFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Polaroid_amount_PolaroidProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Polaroid() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Polaroid(); + var processor = this.Verify>(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyPolaroidFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Polaroid(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.Polaroid(this.rect); + var processor = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs index a4a4f3bb5..5a4402329 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs @@ -1,50 +1,32 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class SaturationTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class SaturationTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData SaturationValues - = new TheoryData - { - 50 , - -50 , - }; - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] - public void ImageShouldApplySaturationFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Saturation_amount_SaturationProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Saturation(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.Saturation(34); + var processor = this.Verify>(); + + Assert.Equal(34, processor.Amount); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] - public void ImageShouldApplySaturationFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Saturation_amount_rect_SaturationProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Saturation(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + this.operations.Saturation(5, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(5, processor.Amount); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs index af554e30d..b2117b94a 100644 --- a/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs @@ -1,43 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.ColorMatrix -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class SepiaTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.ColorMatrix +{ + public class SepiaTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplySepiaFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Sepia_amount_SepiaProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Sepia() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Sepia(); + var processor = this.Verify>(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplySepiaFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Sepia_amount_rect_SepiaProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Sepia(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.Sepia(this.rect); + var processor = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index ef049a539..cd0e8e5ab 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -1,50 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BoxBlurTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class BoxBlurTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData BoxBlurValues - = new TheoryData + [Fact] + public void BoxBlur_BoxBlurProcessorDefaultsSet() { - 3, - 5 - }; + this.operations.BoxBlur(); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.BoxBlur(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(7, processor.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void BoxBlur_amount_BoxBlurProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.BoxBlur(34); + var processor = this.Verify>(); - image.BoxBlur(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + Assert.Equal(34, processor.Radius); + } + + [Fact] + public void BoxBlur_amount_rect_BoxBlurProcessorDefaultsSet() + { + this.operations.BoxBlur(5, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(5, processor.Radius); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index d5817ab14..5a711bd04 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -1,59 +1,70 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; +using System.Collections.Generic; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; - public class DetectEdgesTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class DetectEdgesTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData DetectEdgesFilters - = new TheoryData + + [Fact] + public void DetectEdges_SobelProcessorDefaultsSet() { - EdgeDetection.Kayyali, - EdgeDetection.Kirsch, - EdgeDetection.Lapacian3X3, - EdgeDetection.Lapacian5X5, - EdgeDetection.LaplacianOfGaussian, - EdgeDetection.Prewitt, - EdgeDetection.RobertsCross, - EdgeDetection.Robinson, - EdgeDetection.Scharr, - EdgeDetection.Sobel + this.operations.DetectEdges(); + var processor = this.Verify>(); + + Assert.True(processor.Grayscale); + } + + [Fact] + public void DetectEdges_Rect_SobelProcessorDefaultsSet() + { + this.operations.DetectEdges(this.rect); + var processor = this.Verify>(this.rect); + + Assert.True(processor.Grayscale); + } + public static IEnumerable EdgeDetectionTheoryData => new[] { + new object[]{ new TestType>(), EdgeDetection.Kayyali }, + new object[]{ new TestType>(), EdgeDetection.Kirsch }, + new object[]{ new TestType>(), EdgeDetection.Lapacian3X3 }, + new object[]{ new TestType>(), EdgeDetection.Lapacian5X5 }, + new object[]{ new TestType>(), EdgeDetection.LaplacianOfGaussian }, + new object[]{ new TestType>(), EdgeDetection.Prewitt }, + new object[]{ new TestType>(), EdgeDetection.RobertsCross }, + new object[]{ new TestType>(), EdgeDetection.Robinson }, + new object[]{ new TestType>(), EdgeDetection.Scharr }, + new object[]{ new TestType>(), EdgeDetection.Sobel }, }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(DetectEdgesFilters), DefaultPixelType)] - public void ImageShouldApplyDetectEdgesFilter(TestImageProvider provider, EdgeDetection detector) - where TPixel : struct, IPixel + [MemberData(nameof(EdgeDetectionTheoryData))] + public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetection filter) + where TProcessor : IEdgeDetectorProcessor { - using (Image image = provider.GetImage()) - { - image.DetectEdges(detector) - .DebugSave(provider, detector.ToString(), Extensions.Bmp); - } + this.operations.DetectEdges(filter); + var processor = this.Verify(); + + Assert.True(processor.Grayscale); } [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(DetectEdgesFilters), DefaultPixelType)] - public void ImageShouldApplyDetectEdgesFilterInBox(TestImageProvider provider, EdgeDetection detector) - where TPixel : struct, IPixel + [MemberData(nameof(EdgeDetectionTheoryData))] + public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetection filter) + where TProcessor : IEdgeDetectorProcessor { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.DetectEdges(detector, bounds) - .DebugSave(provider, detector.ToString(), Extensions.Bmp); + var grey = (int)filter % 2 == 0; + this.operations.DetectEdges(filter, grey); + var processor = this.Verify(); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(grey, processor.Grayscale); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index fa4e4b0b6..d0773a0bf 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -1,50 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class GaussianBlurTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class GaussianBlurTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData GaussianBlurValues - = new TheoryData + [Fact] + public void GaussianBlur_GaussianBlurProcessorDefaultsSet() { - 3, - 5 - }; + this.operations.GaussianBlur(); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.GaussianBlur(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(3f, processor.Sigma); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.GaussianBlur(0.2f); + var processor = this.Verify>(); - image.GaussianBlur(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + Assert.Equal(.2f, processor.Sigma); + } + + [Fact] + public void GaussianBlur_amount_rect_GaussianBlurProcessorDefaultsSet() + { + this.operations.GaussianBlur(0.6f, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(.6f, processor.Sigma); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index d15042ee6..c7c78a21a 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -1,50 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Convolution -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class GaussianSharpenTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Convolution +{ + public class GaussianSharpenTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData GaussianSharpenValues - = new TheoryData + [Fact] + public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() { - 3, - 5 - }; + this.operations.GaussianSharpen(); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.GaussianSharpen(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(3f, processor.Sigma); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.GaussianSharpen(0.2f); + var processor = this.Verify>(); - image.GaussianSharpen(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + Assert.Equal(.2f, processor.Sigma); + } + + [Fact] + public void GaussianSharpen_amount_rect_GaussianSharpenProcessorDefaultsSet() + { + this.operations.GaussianSharpen(0.6f, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(.6f, processor.Sigma); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/DelegateTest.cs b/tests/ImageSharp.Tests/Processing/DelegateTest.cs new file mode 100644 index 000000000..518a28bea --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/DelegateTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing +{ + public class DelegateTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Run_CreatedDelegateProcessor() + { + Action> action = (i) => { }; + this.operations.Apply(action); + + DelegateProcessor processor = this.Verify>(); + Assert.Equal(action, processor.Action); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs b/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs index 34a24e70b..9840d71c7 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs @@ -1,50 +1,31 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class AlphaTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class AlphaTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData AlphaValues - = new TheoryData + [Fact] + public void Alpha_amount_AlphaProcessorDefaultsSet() { - 20/100F, - 80/100F - }; + this.operations.Alpha(0.2f); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] - public void ImageShouldApplyAlphaFilter(TestImageProvider provider, float value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Alpha(value) - .DebugSave(provider, value, Extensions.Png); - } + Assert.Equal(.2f, processor.Value); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] - public void ImageShouldApplyAlphaFilterInBox(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [Fact] + public void Alpha_amount_rect_AlphaProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Alpha(value, bounds) - .DebugSave(provider, value, Extensions.Png); + this.operations.Alpha(0.6f, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(.6f, processor.Value); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs index 63efbf3e7..a24b8a4d4 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -1,43 +1,53 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BackgroundColorTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class BackgroundColorTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBackgroundColorFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void BackgroundColor_amount_BackgroundColorProcessorDefaultsSet() + { + this.operations.BackgroundColor(Rgba32.BlanchedAlmond); + var processor = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); + } + + [Fact] + public void BackgroundColor_amount_rect_BackgroundColorProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.BackgroundColor(NamedColors.HotPink) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.rect); + var processor = this.Verify>(this.rect); + + Assert.Equal(GraphicsOptions.Default, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyBackgroundColorFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void BackgroundColor_amount_options_BackgroundColorProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.options); + var processor = this.Verify>(); - image.BackgroundColor(NamedColors.HotPink, bounds) - .DebugSave(provider, null, Extensions.Bmp); + Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); + } + + [Fact] + public void BackgroundColor_amount_rect_options_BackgroundColorProcessorDefaultsSet() + { + this.operations.BackgroundColor(Rgba32.BlanchedAlmond, this.rect, this.options); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(this.options, processor.GraphicsOptions); + Assert.Equal(Rgba32.BlanchedAlmond, processor.Value); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs index c3b37705a..d057f9233 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs @@ -1,50 +1,31 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class BrightnessTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class BrightnessTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData BrightnessValues - = new TheoryData + [Fact] + public void Brightness_amount_BrightnessProcessorDefaultsSet() { - 50, - -50 - }; + this.operations.Brightness(23); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] - public void ImageShouldApplyBrightnessFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Brightness(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(23, processor.Value); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] - public void ImageShouldApplyBrightnessFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Brightness(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + this.operations.Brightness(23, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); ; - } + Assert.Equal(23, processor.Value); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs index 892eb9362..374937ea3 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs @@ -1,50 +1,31 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class ContrastTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class ContrastTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData ContrastValues - = new TheoryData + [Fact] + public void Contrast_amount_ContrastProcessorDefaultsSet() { - 50, - -50 - }; + this.operations.Contrast(23); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] - public void ImageShouldApplyContrastFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Contrast(value) - .DebugSave(provider, value, Extensions.Bmp); - } + Assert.Equal(23, processor.Value); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] - public void ImageShouldApplyContrastFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Contrast_amount_rect_ContrastProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Contrast(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); + this.operations.Contrast(23, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(23, processor.Value); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs index 3d8b3d1a1..ad74b3c90 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs @@ -1,43 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class InvertTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class InvertTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyInvertFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Invert_InvertProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Invert() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Invert(); + var processor = this.Verify>(); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyInvertFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Pixelate_rect_PixelateProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Invert(bounds) - .DebugSave(provider, null, Extensions.Bmp); - - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + this.operations.Invert(this.rect); + var processor = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs index 3d0e8f117..36de67188 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -1,50 +1,52 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class OilPaintTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class OilPaintTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData OilPaintValues - = new TheoryData - { - { 15, 10 }, - { 6, 5 } - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] - public void ImageShouldApplyOilPaintFilter(TestImageProvider provider, int levels, int brushSize) - where TPixel : struct, IPixel + [Fact] + public void OilPaint_OilPaintingProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.OilPaint(levels, brushSize) - .DebugSave(provider, string.Join("-", levels, brushSize), Extensions.Bmp); - } + this.operations.OilPaint(); + var processor = this.Verify>(); + + Assert.Equal(10, processor.Levels); + Assert.Equal(15, processor.BrushSize); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] - public void ImageShouldApplyOilPaintFilterInBox(TestImageProvider provider, int levels, int brushSize) - where TPixel : struct, IPixel + [Fact] + public void OilPaint_rect_OilPaintingProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + this.operations.OilPaint(this.rect); + var processor = this.Verify>(this.rect); - image.OilPaint(levels, brushSize, bounds) - .DebugSave(provider, string.Join("-", levels, brushSize), Extensions.Bmp); + Assert.Equal(10, processor.Levels); + Assert.Equal(15, processor.BrushSize); + } + [Fact] + public void OilPaint_Levels_Brsuh_OilPaintingProcessorDefaultsSet() + { + this.operations.OilPaint(34, 65); + var processor = this.Verify>(); + + Assert.Equal(34, processor.Levels); + Assert.Equal(65, processor.BrushSize); + } + + [Fact] + public void OilPaint_Levels_Brsuh_rect_OilPaintingProcessorDefaultsSet() + { + this.operations.OilPaint(54, 43, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F); - } + Assert.Equal(54, processor.Levels); + Assert.Equal(43, processor.BrushSize); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs index 253f1f459..8b50e1486 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -1,84 +1,40 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Effects -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class PixelateTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class PixelateTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData PixelateValues - = new TheoryData - { - 4 , - 8 - }; - - [Theory] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] - public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Pixelate_PixelateProcessorDefaultsSet() { - using (Image image = provider.GetImage()) - { - image.Pixelate(value) - .DebugSave(provider, value, Extensions.Bmp); + this.operations.Pixelate(); + var processor = this.Verify>(); - // Test the neigbouring pixels - for (int y = 0; y < image.Height; y += value) - { - for (int x = 0; x < image.Width; x += value) - { - TPixel source = image[x, y]; - for (int pixY = y; pixY < y + value && pixY < image.Height; pixY++) - { - for (int pixX = x; pixX < x + value && pixX < image.Width; pixX++) - { - Assert.Equal(source, image[pixX, pixY]); - } - } - } - } - } + Assert.Equal(4, processor.Size); } - [Theory] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] - public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + [Fact] + public void Pixelate_Size_PixelateProcessorDefaultsSet() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + this.operations.Pixelate(12); + var processor = this.Verify>(); - image.Pixelate(value, bounds) - .DebugSave(provider, value, Extensions.Bmp); - - for (int y = 0; y < image.Height; y++) - { - for (int x = 0; x < image.Width; x++) - { - int tx = x; - int ty = y; - TPixel sourceColor = source[tx, ty]; - if (bounds.Contains(tx, ty)) - { - int sourceX = tx - ((tx - bounds.Left) % value) + (value / 2); - int sourceY = ty - ((ty - bounds.Top) % value) + (value / 2); + Assert.Equal(12, processor.Size); + } - sourceColor = image[sourceX, sourceY]; - } - Assert.Equal(sourceColor, image[tx, ty]); - } - } + [Fact] + public void Pixelate_Size_rect_PixelateProcessorDefaultsSet() + { + this.operations.Pixelate(23, this.rect); + var processor = this.Verify>(this.rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(23, processor.Size); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs index d0633dca5..9e7a04650 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -1,67 +1,59 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Overlays -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; - public class GlowTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Overlays +{ + public class GlowTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_GlowProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Glow() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Glow(); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterColor(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_Color_GlowProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Glow(NamedColors.Orange) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Glow(Rgba32.Aquamarine); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Aquamarine, p.GlowColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterRadius(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_Radux_GlowProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Glow(image.Width / 4F) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Glow(3.5f); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(ValueSize.Absolute(3.5f), p.Radius); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyGlowFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Glow_Rect_GlowProcessorWithDefaultValues() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Glow(bounds) - .DebugSave(provider, null, Extensions.Bmp); + var rect = new Rectangle(12, 123, 43, 65); + this.operations.Glow(rect); + var p = this.Verify>(rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.GlowColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.Radius); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs index 56fcf0ee0..e082e42cd 100644 --- a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -1,67 +1,64 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Overlays -{ - using ImageSharp.PixelFormats; - using SixLabors.Primitives; - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; +using SixLabors.Primitives; +using Xunit; - public class VignetteTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Overlays +{ + public class VignetteTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilter(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_VignetteProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Vignette() - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Vignette(); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); + Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterColor(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_Color_VignetteProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Vignette(NamedColors.Orange) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Vignette(Rgba32.Aquamarine); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Aquamarine, p.VignetteColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); + Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterRadius(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_Radux_VignetteProcessorWithDefaultValues() { - using (Image image = provider.GetImage()) - { - image.Vignette(image.Width / 4F, image.Height / 4F) - .DebugSave(provider, null, Extensions.Bmp); - } + this.operations.Vignette(3.5f, 12123f); + var p = this.Verify>(); + + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(ValueSize.Absolute(3.5f), p.RadiusX); + Assert.Equal(ValueSize.Absolute(12123f), p.RadiusY); } - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldApplyVignetteFilterInBox(TestImageProvider provider) - where TPixel : struct, IPixel + [Fact] + public void Vignette_Rect_VignetteProcessorWithDefaultValues() { - using (Image source = provider.GetImage()) - using (var image = new Image(source)) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Vignette(bounds) - .DebugSave(provider, null, Extensions.Bmp); + var rect = new Rectangle(12, 123, 43, 65); + this.operations.Vignette(rect); + var p = this.Verify>(rect); - ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); - } + Assert.Equal(GraphicsOptions.Default, p.GraphicsOptions); + Assert.Equal(Rgba32.Black, p.VignetteColor); + Assert.Equal(ValueSize.PercentageOfWidth(.5f), p.RadiusX); + Assert.Equal(ValueSize.PercentageOfHeight(.5f), p.RadiusY); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs new file mode 100644 index 000000000..3daeecfc7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization +{ + public class BinaryThresholdTest : FileTestBase + { + public static readonly TheoryData BinaryThresholdValues + = new TheoryData + { + .25F, + .75F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + public void ImageShouldApplyBinaryThresholdFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BinaryThreshold(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BinaryThreshold(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs new file mode 100644 index 000000000..9a6d24226 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Dithering; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization +{ + using System.Linq; + + public class DitherTests : FileTestBase + { + public static readonly string[] CommonTestImages = + { + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike + }; + + public static readonly TheoryData Ditherers = new TheoryData + { + { "Ordered", new OrderedDither() }, + { "Bayer", new BayerDither() } + }; + + public static readonly TheoryData ErrorDiffusers = new TheoryData + { + { "Atkinson", new AtkinsonDiffuser() }, + { "Burks", new BurksDiffuser() }, + { "FloydSteinberg", new FloydSteinbergDiffuser() }, + { "JarvisJudiceNinke", new JarvisJudiceNinkeDiffuser() }, + { "Sierra2", new Sierra2Diffuser() }, + { "Sierra3", new Sierra3Diffuser() }, + { "SierraLite", new SierraLiteDiffuser() }, + { "Stucki", new StuckiDiffuser() }, + }; + + + private static IOrderedDither DefaultDitherer => new OrderedDither(); + + private static IErrorDiffuser DefaultErrorDiffuser => new AtkinsonDiffuser(); + + [Theory] + [WithFileCollection(nameof(CommonTestImages), nameof(Ditherers), DefaultPixelType)] + [WithTestPatternImages(nameof(Ditherers), 100, 100, DefaultPixelType)] + public void DitherFilter_WorksWithAllDitherers(TestImageProvider provider, string name, IOrderedDither ditherer) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(ditherer)); + image.DebugSave(provider, name); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), nameof(ErrorDiffusers), DefaultPixelType)] + [WithTestPatternImages(nameof(ErrorDiffusers), 100, 100, DefaultPixelType)] + public void DiffusionFilter_WorksWithAllErrorDiffusers(TestImageProvider provider, string name, IErrorDiffuser diffuser) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(diffuser, .5F)); + image.DebugSave(provider, name); + } + } + + [Theory] + [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + public void DitherFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(DefaultDitherer)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)] + public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Dither(DefaultErrorDiffuser, 0.5f)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + public void ApplyDitherFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Dither(DefaultDitherer, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, DefaultPixelType)] + public void ApplyDiffusionFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Dither(DefaultErrorDiffuser, .5F, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs new file mode 100644 index 000000000..c3250ccfc --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class BlackWhiteTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBlackWhiteFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BlackWhite()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBlackWhiteFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BlackWhite(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs new file mode 100644 index 000000000..122ff5a64 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class ColorBlindnessTest : FileTestBase + { + public static readonly TheoryData ColorBlindnessFilters + = new TheoryData + { + ColorBlindness.Achromatomaly, + ColorBlindness.Achromatopsia, + ColorBlindness.Deuteranomaly, + ColorBlindness.Deuteranopia, + ColorBlindness.Protanomaly, + ColorBlindness.Protanopia, + ColorBlindness.Tritanomaly, + ColorBlindness.Tritanopia + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] + public void ImageShouldApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.ColorBlindness(colorBlindness)); + image.DebugSave(provider, colorBlindness.ToString()); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] + public void ImageShouldApplyColorBlindnessFilterInBox(TestImageProvider provider, ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.ColorBlindness(colorBlindness, bounds)); + image.DebugSave(provider, colorBlindness.ToString()); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs new file mode 100644 index 000000000..fe003da32 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs @@ -0,0 +1,63 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class GrayscaleTest : FileTestBase + { + public static readonly TheoryData GrayscaleModeTypes + = new TheoryData + { + GrayscaleMode.Bt601, + GrayscaleMode.Bt709 + }; + + /// + /// Use test patterns over loaded images to save decode time. + /// + [Theory] + [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] + public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Grayscale(value)); + byte[] data = new byte[3]; + System.Span span = image.Frames.RootFrame.GetPixelSpan(); + for (int i = 0; i < span.Length; i++) + { + span[i].ToXyzBytes(data, 0); + Assert.Equal(data[0], data[1]); + Assert.Equal(data[1], data[2]); + } + + image.DebugSave(provider, value.ToString()); + } + } + + [Theory] + [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] + public void ImageShouldApplyGrayscaleFilterInBox(TestImageProvider provider, GrayscaleMode value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.Mutate(x => x.Grayscale(value, bounds)); + image.DebugSave(provider, value.ToString()); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs new file mode 100644 index 000000000..5a1ea16a7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class HueTest : FileTestBase + { + public static readonly TheoryData HueValues + = new TheoryData + { + 180, + -180 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] + public void ImageShouldApplyHueFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Hue(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] + public void ImageShouldApplyHueFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Hue(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs new file mode 100644 index 000000000..ebf163ab8 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class KodachromeTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyKodachromeFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Kodachrome()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyKodachromeFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Kodachrome(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs new file mode 100644 index 000000000..48a58580a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class LomographTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyLomographFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Lomograph()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyLomographFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Lomograph(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs new file mode 100644 index 000000000..2e61e3f02 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class PolaroidTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyPolaroidFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Polaroid()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyPolaroidFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Polaroid(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs new file mode 100644 index 000000000..2532a7fe7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class SaturationTest : FileTestBase + { + public static readonly TheoryData SaturationValues + = new TheoryData + { + 50 , + -50 , + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] + public void ImageShouldApplySaturationFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Saturation(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] + public void ImageShouldApplySaturationFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Saturation(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs new file mode 100644 index 000000000..28b1f6256 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.ColorMatrix +{ + public class SepiaTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplySepiaFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Sepia()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplySepiaFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Sepia(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs new file mode 100644 index 000000000..94af7aa37 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class BoxBlurTest : FileTestBase + { + public static readonly TheoryData BoxBlurValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] + public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BoxBlur(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] + public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BoxBlur(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs new file mode 100644 index 000000000..54686cb4c --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -0,0 +1,89 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class DetectEdgesTest : FileTestBase + { + public static readonly string[] CommonTestImages = { TestImages.Png.Bike }; + + public static readonly TheoryData DetectEdgesFilters = new TheoryData + { + EdgeDetection.Kayyali, + EdgeDetection.Kirsch, + EdgeDetection.Lapacian3X3, + EdgeDetection.Lapacian5X5, + EdgeDetection.LaplacianOfGaussian, + EdgeDetection.Prewitt, + EdgeDetection.RobertsCross, + EdgeDetection.Robinson, + EdgeDetection.Scharr, + EdgeDetection.Sobel + }; + + [Theory] + [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, DefaultPixelType)] + [WithFileCollection(nameof(CommonTestImages), nameof(DetectEdgesFilters), DefaultPixelType)] + public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetection detector) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges(detector)); + image.DebugSave(provider, detector.ToString()); + image.CompareToReferenceOutput(provider, detector.ToString()); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), CommonNonDefaultPixelTypes)] + public void DetectEdges_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges()); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + public void DetectEdges_IsAppliedToAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.DetectEdges()); + image.DebugSave(provider, extension: "gif"); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void DetectEdges_InBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.DetectEdges(bounds)); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + + // TODO: We don't need this any longer after switching to ReferenceImages + //ImageComparer.Tolerant().EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs new file mode 100644 index 000000000..3508d544b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class GaussianBlurTest : FileTestBase + { + public static readonly TheoryData GaussianBlurValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] + public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.GaussianBlur(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] + public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.GaussianBlur(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs new file mode 100644 index 000000000..4775444a5 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + public class GaussianSharpenTest : FileTestBase + { + public static readonly TheoryData GaussianSharpenValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] + public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.GaussianSharpen(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] + public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.GaussianSharpen(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs new file mode 100644 index 000000000..7f2840f2c --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class AlphaTest : FileTestBase + { + public static readonly TheoryData AlphaValues + = new TheoryData + { + 20/100F, + 80/100F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] + public void ImageShouldApplyAlphaFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Alpha(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] + public void ImageShouldApplyAlphaFilterInBox(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Alpha(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs new file mode 100644 index 000000000..2dd44b3a7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class BackgroundColorTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBackgroundColorFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.BackgroundColor(NamedColors.HotPink)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBackgroundColorFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.BackgroundColor(NamedColors.HotPink, bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs new file mode 100644 index 000000000..9bfed05b9 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class BrightnessTest : FileTestBase + { + public static readonly TheoryData BrightnessValues + = new TheoryData + { + 50, + -50 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] + public void ImageShouldApplyBrightnessFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Brightness(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] + public void ImageShouldApplyBrightnessFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Brightness(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs new file mode 100644 index 000000000..f1e33db88 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class ContrastTest : FileTestBase + { + public static readonly TheoryData ContrastValues + = new TheoryData + { + 50, + -50 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] + public void ImageShouldApplyContrastFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Contrast(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] + public void ImageShouldApplyContrastFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Contrast(value, bounds)); + image.DebugSave(provider, value); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs new file mode 100644 index 000000000..593023677 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class InvertTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyInvertFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Invert()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyInvertFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Invert(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs new file mode 100644 index 000000000..608fcf10c --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class OilPaintTest : FileTestBase + { + public static readonly TheoryData OilPaintValues + = new TheoryData + { + { 15, 10 }, + { 6, 5 } + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] + public void ApplyOilPaintFilter(TestImageProvider provider, int levels, int brushSize) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.OilPaint(levels, brushSize)); + image.DebugSave(provider, string.Join("-", levels, brushSize)); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] + public void ApplyOilPaintFilterInBox(TestImageProvider provider, int levels, int brushSize) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.OilPaint(levels, brushSize, bounds)); + image.DebugSave(provider, string.Join("-", levels, brushSize)); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs new file mode 100644 index 000000000..b3f1ee3a2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Effects +{ + public class PixelateTest : FileTestBase + { + public static readonly TheoryData PixelateValues + = new TheoryData + { + 4 , + 8 + }; + + [Theory] + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] + public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Pixelate(value)); + image.DebugSave(provider, value); + + // Test the neigbouring pixels + for (int y = 0; y < image.Height; y += value) + { + for (int x = 0; x < image.Width; x += value) + { + TPixel source = image[x, y]; + for (int pixY = y; pixY < y + value && pixY < image.Height; pixY++) + { + for (int pixX = x; pixX < x + value && pixX < image.Width; pixX++) + { + Assert.Equal(source, image[pixX, pixY]); + } + } + } + } + } + } + + [Theory] + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] + public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Pixelate(value, bounds)); + image.DebugSave(provider, value); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + int tx = x; + int ty = y; + TPixel sourceColor = source[tx, ty]; + if (bounds.Contains(tx, ty)) + { + int sourceX = tx - ((tx - bounds.Left) % value) + (value / 2); + int sourceY = ty - ((ty - bounds.Top) % value) + (value / 2); + + sourceColor = image[sourceX, sourceY]; + } + Assert.Equal(sourceColor, image[tx, ty]); + } + } + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs new file mode 100644 index 000000000..5693b6d75 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays +{ + public class GlowTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Glow()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Glow(NamedColors.Orange)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Glow(image.Width / 4F)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Glow(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs new file mode 100644 index 000000000..0d9c3e89b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Overlays +{ + public class VignetteTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Vignette()); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Vignette(NamedColors.Orange)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Vignette(image.Width / 4F, image.Height / 4F)); + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = source.Clone()) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Vignette(bounds)); + image.DebugSave(provider); + + ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs new file mode 100644 index 000000000..161ac15e3 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class AutoOrientTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData OrientationValues + = new TheoryData + { + { RotateType.None, FlipType.None, 0 }, + { RotateType.None, FlipType.None, 1 }, + { RotateType.None, FlipType.Horizontal, 2 }, + { RotateType.Rotate180, FlipType.None, 3 }, + { RotateType.Rotate180, FlipType.Horizontal, 4 }, + { RotateType.Rotate90, FlipType.Horizontal, 5 }, + { RotateType.Rotate270, FlipType.None, 6 }, + { RotateType.Rotate90, FlipType.Vertical, 7 }, + { RotateType.Rotate90, FlipType.None, 8 }, + }; + + public static readonly TheoryData InvalidOrientationValues + = new TheoryData + { + { ExifDataType.Byte, new byte[] { 1 } }, + { ExifDataType.SignedByte, new byte[] { 2 } }, + { ExifDataType.SignedShort, BitConverter.GetBytes((short) 3) }, + { ExifDataType.Long, BitConverter.GetBytes((uint) 4) }, + { ExifDataType.SignedLong, BitConverter.GetBytes((int) 5) } + }; + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)] + public void ImageShouldAutoRotate(TestImageProvider provider, RotateType rotateType, FlipType flipType, ushort orientation) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.MetaData.ExifProfile = new ExifProfile(); + image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); + + image.Mutate(x => x.RotateFlip(rotateType, flipType)); + image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before")); + + image.Mutate(x => x.AutoOrient()); + image.DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "2_after")); + } + } + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(InvalidOrientationValues), DefaultPixelType)] + public void ImageShouldAutoRotateInvalidValues(TestImageProvider provider, ExifDataType dataType, byte[] orientation) + where TPixel : struct, IPixel + { + var profile = new ExifProfile(); + profile.SetValue(ExifTag.JPEGTables, orientation); + + byte[] bytes = profile.ToByteArray(); + // Change the tag into ExifTag.Orientation + bytes[16] = 18; + bytes[17] = 1; + // Change the data type + bytes[18] = (byte)dataType; + // Change the number of components + bytes[20] = 1; + + using (Image image = provider.GetImage()) + { + image.MetaData.ExifProfile = new ExifProfile(bytes); + image.Mutate(x=>x.AutoOrient()); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs new file mode 100644 index 000000000..aa18feac2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class CropTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldCrop(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Crop(image.Width / 2, image.Height / 2)); + image.DebugSave(provider); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs new file mode 100644 index 000000000..57341560e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/EntropyCropTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class EntropyCropTest : FileTestBase + { + public static readonly TheoryData EntropyCropValues + = new TheoryData + { + .25F, + .75F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(EntropyCropValues), DefaultPixelType)] + public void ImageShouldEntropyCrop(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.EntropyCrop(value)); + image.DebugSave(provider, value); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs new file mode 100644 index 000000000..d4de4c3d2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class FlipTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData FlipValues + = new TheoryData + { + { FlipType.None }, + { FlipType.Vertical }, + { FlipType.Horizontal }, + }; + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(FlipValues), DefaultPixelType)] + public void ImageShouldFlip(TestImageProvider provider, FlipType flipType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Flip(flipType)); + image.DebugSave(provider, flipType, Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs new file mode 100644 index 000000000..b5f26b488 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class PadTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldPad(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50)); + image.DebugSave(provider); + + // Check pixels are empty + for (int y = 0; y < 25; y++) + { + for (int x = 0; x < 25; x++) + { + Assert.Equal(default(TPixel), image[x, y]); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs similarity index 77% rename from tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs rename to tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs index a300672e8..963b849d2 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs @@ -1,20 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using System; - using System.IO; - using System.Text; - - using ImageSharp.Processing; - using ImageSharp.Processing.Processors; - - using Xunit; - using Xunit.Abstractions; +using System; +using System.IO; +using System.Text; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ public class ResizeProfilingBenchmarks : MeasureFixture { public ResizeProfilingBenchmarks(ITestOutputHelper output) @@ -34,7 +30,7 @@ namespace ImageSharp.Tests.Processing.Transforms { using (var image = new Image(width, height)) { - image.Resize(width / 4, height / 4); + image.Mutate(x => x.Resize(width / 4, height / 4)); } }); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs new file mode 100644 index 000000000..820ca8356 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -0,0 +1,335 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using SixLabors.Primitives; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class ResizeTests : FileTestBase + { + public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; + + public static readonly TheoryData AllReSamplers = + new TheoryData + { + { "Bicubic", new BicubicResampler() }, + { "Triangle", new TriangleResampler() }, + { "NearestNeighbor", new NearestNeighborResampler() }, + { "Box", new BoxResampler() }, + { "Lanczos3", new Lanczos3Resampler() }, + { "Lanczos5", new Lanczos5Resampler() }, + { "MitchellNetravali", new MitchellNetravaliResampler() }, + { "Lanczos8", new Lanczos8Resampler() }, + { "Hermite", new HermiteResampler() }, + { "Spline", new SplineResampler() }, + { "Robidoux", new RobidouxResampler() }, + { "RobidouxSharp", new RobidouxSharpResampler() }, + { "Welch", new WelchResampler() } + }; + + [Theory] + [WithTestPatternImages(nameof(AllReSamplers), 100, 100, DefaultPixelType, 0.5f)] + [WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.5f)] + [WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.3f)] + public void Resize_WorksWithAllResamplers(TestImageProvider provider, string name, IResampler sampler, float ratio) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + SizeF newSize = image.Size() * ratio; + image.Mutate(x => x.Resize((Size)newSize, sampler, false)); + string details = $"{name}-{ratio}"; + + image.DebugSave(provider, details); + image.CompareToReferenceOutput(provider, details); + } + } + + [Theory] + [WithTestPatternImages(100, 100, DefaultPixelType)] + public void Resize_Compand(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Size() / 2, true)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithTestPatternImages(50, 50, CommonNonDefaultPixelTypes)] + public void Resize_IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, true)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + public void Resize_IsAppliedToAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, true)); + + // Comparer fights decoder with gif-s. Could not use CompareToReferenceOutput here :( + image.DebugSave(provider, extension: Extensions.Gif); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeFromSourceRectangle(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); + var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Mutate(x => x.Resize(image.Width, image.Height, new BicubicResampler(), sourceRectangle, destRectangle, false)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWidthAndKeepAspect(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(image.Width / 3, 0, false)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeHeightAndKeepAspect(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(0, image.Height / 3, false)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithCropWidthMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width / 2, image.Height) + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithCropHeightMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width, image.Height / 2) + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithPadMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width + 200, image.Height), + Mode = ResizeMode.Pad + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithBoxPadMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width + 200, image.Height + 200), + Mode = ResizeMode.BoxPad + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithMaxMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(300, 300), + Mode = ResizeMode.Max + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithMinMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size((int)MathF.Round(image.Width * .75F), (int)MathF.Round(image.Height * .95F)), + Mode = ResizeMode.Min + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + public void ResizeWithStretchMode(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Size = new Size(image.Width / 2, image.Height), + Mode = ResizeMode.Stretch + }; + + image.Mutate(x => x.Resize(options)); + + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void BicubicWindowOscillatesCorrectly(float x, float expected) + { + var sampler = new BicubicResampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void TriangleWindowOscillatesCorrectly(float x, float expected) + { + var sampler = new TriangleResampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) + { + var sampler = new Lanczos3Resampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-4, 0)] + [InlineData(-2, 0)] + [InlineData(0, 1)] + [InlineData(2, 0)] + [InlineData(4, 0)] + public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) + { + var sampler = new Lanczos5Resampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs new file mode 100644 index 000000000..e7415e161 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateFlipTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class RotateFlipTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData RotateFlipValues + = new TheoryData + { + { RotateType.None, FlipType.Vertical }, + { RotateType.None, FlipType.Horizontal }, + { RotateType.Rotate90, FlipType.None }, + { RotateType.Rotate180, FlipType.None }, + { RotateType.Rotate270, FlipType.None }, + }; + + [Theory] + [WithTestPatternImages(nameof(RotateFlipValues), 100, 50, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateFlipValues), 50, 100, DefaultPixelType)] + public void RotateFlip(TestImageProvider provider, RotateType rotateType, FlipType flipType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.RotateFlip(rotateType, flipType)); + image.DebugSave(provider, string.Join("_", rotateType, flipType)); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs new file mode 100644 index 000000000..161af43c9 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class RotateTests : FileTestBase + { + public static readonly TheoryData RotateFloatValues + = new TheoryData + { + 170, + -170 + }; + + public static readonly TheoryData RotateEnumValues + = new TheoryData + { + RotateType.None, + RotateType.Rotate90, + RotateType.Rotate180, + RotateType.Rotate270 + }; + + [Theory] + [WithTestPatternImages(nameof(RotateFloatValues), 100, 50, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateFloatValues), 50, 100, DefaultPixelType)] + public void Rotate(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Rotate(value)); + image.DebugSave(provider, value); + } + } + + [Theory] + [WithTestPatternImages(nameof(RotateEnumValues), 100, 50, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateEnumValues), 50, 100, DefaultPixelType)] + public void Rotate_WithRotateTypeEnum(TestImageProvider provider, RotateType value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Rotate(value)); + image.DebugSave(provider, value); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs new file mode 100644 index 000000000..5e83fa620 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public class SkewTest : FileTestBase + { + public static readonly TheoryData SkewValues + = new TheoryData + { + { 20, 10 }, + { -20, -10 } + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(SkewValues), DefaultPixelType)] + public void ImageShouldSkew(TestImageProvider provider, float x, float y) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(i => i.Skew(x, y)); + image.DebugSave(provider, string.Join("_", x, y)); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs index 0646dc937..20de25054 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs @@ -1,83 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using System; - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class AutoOrientTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class AutoOrientTests : BaseImageOperationsExtensionTest { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData OrientationValues - = new TheoryData - { - { RotateType.None, FlipType.None, 0 }, - { RotateType.None, FlipType.None, 1 }, - { RotateType.None, FlipType.Horizontal, 2 }, - { RotateType.Rotate180, FlipType.None, 3 }, - { RotateType.Rotate180, FlipType.Horizontal, 4 }, - { RotateType.Rotate90, FlipType.Horizontal, 5 }, - { RotateType.Rotate270, FlipType.None, 6 }, - { RotateType.Rotate90, FlipType.Vertical, 7 }, - { RotateType.Rotate90, FlipType.None, 8 }, - }; - - public static readonly TheoryData InvalidOrientationValues - = new TheoryData - { - { ExifDataType.Byte, new byte[] { 1 } }, - { ExifDataType.SignedByte, new byte[] { 2 } }, - { ExifDataType.SignedShort, BitConverter.GetBytes((short) 3) }, - { ExifDataType.Long, BitConverter.GetBytes((uint) 4) }, - { ExifDataType.SignedLong, BitConverter.GetBytes((int) 5) } - }; - - [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)] - public void ImageShouldAutoRotate(TestImageProvider provider, RotateType rotateType, FlipType flipType, ushort orientation) - where TPixel : struct, IPixel + [Fact] + public void AutoOrient_AutoOrientProcessor() { - using (Image image = provider.GetImage()) - { - image.MetaData.ExifProfile = new ExifProfile(); - image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); - - image.RotateFlip(rotateType, flipType) - .DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before"), Extensions.Bmp) - .AutoOrient() - .DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "2_after"), Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(InvalidOrientationValues), DefaultPixelType)] - public void ImageShouldAutoRotateInvalidValues(TestImageProvider provider, ExifDataType dataType, byte[] orientation) - where TPixel : struct, IPixel - { - var profile = new ExifProfile(); - profile.SetValue(ExifTag.JPEGTables, orientation); - - byte[] bytes = profile.ToByteArray(); - // Change the tag into ExifTag.Orientation - bytes[16] = 18; - bytes[17] = 1; - // Change the data type - bytes[18] = (byte)dataType; - // Change the number of components - bytes[20] = 1; - - using (Image image = provider.GetImage()) - { - image.MetaData.ExifProfile = new ExifProfile(bytes); - image.AutoOrient(); - } + this.operations.AutoOrient(); + this.Verify>(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index ca20abf79..a001efc41 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -1,26 +1,37 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms { - using ImageSharp.PixelFormats; + public class CropTest : BaseImageOperationsExtensionTest + { + [Theory] + [InlineData(10, 10)] + [InlineData(12, 123)] + public void Crop_Width_height_CropProcessorWithRectangleSet(int width, int height) + { + this.operations.Crop(width, height); + var processor = this.Verify>(); - using Xunit; + Assert.Equal(new Rectangle(0, 0, width, height), processor.CropRectangle); + } - public class CropTest : FileTestBase - { [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldCrop(TestImageProvider provider) - where TPixel : struct, IPixel + [InlineData(10, 10, 2, 6)] + [InlineData(12, 123, 6, 2)] + public void Crop_Rectangle_CropProcessorWithRectangleSet(int x, int y, int width, int height) { - using (Image image = provider.GetImage()) - { - image.Crop(image.Width / 2, image.Height / 2) - .DebugSave(provider, null, Extensions.Bmp); - } + var rect = new Rectangle(x, y, width, height); + this.operations.Crop(rect); + var processor = this.Verify>(); + + Assert.Equal(rect, processor.CropRectangle); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs index 24febd5b2..c1cde4879 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs @@ -1,33 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class EntropyCropTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class EntropyCropTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData EntropyCropValues - = new TheoryData - { - .25F, - .75F - }; [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(EntropyCropValues), DefaultPixelType)] - public void ImageShouldEntropyCrop(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [InlineData(0.5f)] + [InlineData(.2f)] + public void EntropyCrop_threasholdFloat_EntropyCropProcessorWithThreshold(float threashold) { - using (Image image = provider.GetImage()) - { - image.EntropyCrop(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.EntropyCrop(threashold); + var processor = this.Verify>(); + + Assert.Equal(threashold, processor.Threshold); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index 45ab1e5f8..4b3e97d10 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -1,37 +1,27 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class FlipTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class FlipTests : BaseImageOperationsExtensionTest { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData FlipValues - = new TheoryData - { - { FlipType.None }, - { FlipType.Vertical }, - { FlipType.Horizontal }, - }; [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(FlipValues), DefaultPixelType)] - public void ImageShouldFlip(TestImageProvider provider, FlipType flipType) - where TPixel : struct, IPixel + [InlineData(FlipType.None)] + [InlineData(FlipType.Horizontal)] + [InlineData(FlipType.Vertical)] + public void Flip_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(FlipType flip) { - using (Image image = provider.GetImage()) - { - image.Flip(flipType) - .DebugSave(provider, flipType, Extensions.Bmp); - } + this.operations.Flip(flip); + var flipProcessor = this.Verify>(); + + Assert.Equal(flip, flipProcessor.FlipType); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 7caa1e7c0..58fc7fa80 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,35 +1,20 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; - public class PadTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class PadTest : BaseImageOperationsExtensionTest { - [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldPad(TestImageProvider provider) - where TPixel : struct, IPixel +#pragma warning disable xUnit1004 // Test methods should not be skipped + [Fact(Skip = "Skip this is a helper around resize, skip until resize can be refactord")] +#pragma warning restore xUnit1004 // Test methods should not be skipped + public void Pad_width_height_ResizeProcessorWithCorrectOPtionsSet() { - using (Image image = provider.GetImage()) - { - image.Pad(image.Width + 50, image.Height + 50) - .DebugSave(provider, null, Extensions.Bmp); - - // Check pixels are empty - for (int y = 0; y < 25; y++) - { - for (int x = 0; x < 25; x++) - { - Assert.Equal(default(TPixel), image[x, y]); - } - } - } + throw new NotImplementedException("Write test here"); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index fb195254e..d2f5cb2c3 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -1,273 +1,23 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - using SixLabors.Primitives; - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; +using Xunit; - public class ResizeTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class ResizeTests : BaseImageOperationsExtensionTest { - public static readonly string[] ResizeFiles = { TestImages.Jpeg.Baseline.Calliphora }; - - public static readonly TheoryData ReSamplers = - new TheoryData - { - { "Bicubic", new BicubicResampler() }, - { "Triangle", new TriangleResampler() }, - { "NearestNeighbor", new NearestNeighborResampler() }, - { "Box", new BoxResampler() }, - { "Lanczos3", new Lanczos3Resampler() }, - { "Lanczos5", new Lanczos5Resampler() }, - { "MitchellNetravali", new MitchellNetravaliResampler() }, - { "Lanczos8", new Lanczos8Resampler() }, - { "Hermite", new HermiteResampler() }, - { "Spline", new SplineResampler() }, - { "Robidoux", new RobidouxResampler() }, - { "RobidouxSharp", new RobidouxSharpResampler() }, - { "Welch", new WelchResampler() } - }; - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResize(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Resize(image.Width / 2, image.Height / 2, sampler, true) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeFromSourceRectangle(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); - var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - - image.Resize(image.Width, image.Height, sampler, sourceRectangle, destRectangle, false) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWidthAndKeepAspect(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Resize(image.Width / 3, 0, sampler, false) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeHeightAndKeepAspect(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Resize(0, image.Height / 3, sampler, false) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithCropWidthMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width / 2, image.Height) - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithCropHeightMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width, image.Height / 2) - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithPadMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width + 200, image.Height), - Mode = ResizeMode.Pad - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithBoxPadMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel +#pragma warning disable xUnit1004 // Test methods should not be skipped + [Fact(Skip = "Skip resize tests as they need refactoring to be simpler and just pass data into the resize processor.")] +#pragma warning restore xUnit1004 // Test methods should not be skipped + public void TestMissing() { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width + 200, image.Height + 200), - Mode = ResizeMode.BoxPad - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithMaxMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(300, 300), - Mode = ResizeMode.Max - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithMinMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size((int)MathF.Round(image.Width * .75F), (int)MathF.Round(image.Height * .95F)), - Mode = ResizeMode.Min - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] - public void ImageShouldResizeWithStretchMode(TestImageProvider provider, string name, IResampler sampler) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - var options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width / 2, image.Height), - Mode = ResizeMode.Stretch - }; - - image.Resize(options) - .DebugSave(provider, name, Extensions.Bmp); - } - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void BicubicWindowOscillatesCorrectly(float x, float expected) - { - var sampler = new BicubicResampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void TriangleWindowOscillatesCorrectly(float x, float expected) - { - var sampler = new TriangleResampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) - { - var sampler = new Lanczos3Resampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-4, 0)] - [InlineData(-2, 0)] - [InlineData(0, 1)] - [InlineData(2, 0)] - [InlineData(4, 0)] - public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) - { - var sampler = new Lanczos5Resampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); + // + throw new NotImplementedException("Write test here"); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs index f85ef6f13..67602131b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs @@ -1,39 +1,39 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class RotateFlipTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class RotateFlipTests : BaseImageOperationsExtensionTest { - public static readonly string[] FlipFiles = { TestImages.Bmp.F }; - - public static readonly TheoryData RotateFlipValues - = new TheoryData - { - { RotateType.None, FlipType.Vertical }, - { RotateType.None, FlipType.Horizontal }, - { RotateType.Rotate90, FlipType.None }, - { RotateType.Rotate180, FlipType.None }, - { RotateType.Rotate270, FlipType.None }, - }; [Theory] - [WithFileCollection(nameof(FlipFiles), nameof(RotateFlipValues), DefaultPixelType)] - public void ImageShouldRotateFlip(TestImageProvider provider, RotateType rotateType, FlipType flipType) - where TPixel : struct, IPixel + [InlineData(RotateType.None, FlipType.None, 0)] + [InlineData(RotateType.Rotate90, FlipType.None, 90)] + [InlineData(RotateType.Rotate180, FlipType.None, 180)] + [InlineData(RotateType.Rotate270, FlipType.None, 270)] + [InlineData(RotateType.None, FlipType.Horizontal, 0)] + [InlineData(RotateType.Rotate90, FlipType.Horizontal, 90)] + [InlineData(RotateType.Rotate180, FlipType.Horizontal, 180)] + [InlineData(RotateType.Rotate270, FlipType.Horizontal, 270)] + [InlineData(RotateType.None, FlipType.Vertical, 0)] + [InlineData(RotateType.Rotate90, FlipType.Vertical, 90)] + [InlineData(RotateType.Rotate180, FlipType.Vertical, 180)] + [InlineData(RotateType.Rotate270, FlipType.Vertical, 270)] + public void Rotate_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(RotateType angle, FlipType flip, float expectedAngle) { - using (Image image = provider.GetImage()) - { - image.RotateFlip(rotateType, flipType) - .DebugSave(provider, string.Join("_", rotateType, flipType), Extensions.Bmp); - } + this.operations.RotateFlip(angle, flip); + var rotateProcessor = this.Verify>(0); + var flipProcessor = this.Verify>(1); + + Assert.Equal(expectedAngle, rotateProcessor.Angle); + Assert.False(rotateProcessor.Expand); + Assert.Equal(flip, flipProcessor.FlipType); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs index 1f1856429..80eccd00c 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs @@ -1,55 +1,55 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - using ImageSharp.Processing; - - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class RotateTests : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class RotateTests : BaseImageOperationsExtensionTest { - public static readonly TheoryData RotateFloatValues - = new TheoryData - { - 170, - -170 - }; - public static readonly TheoryData RotateEnumValues - = new TheoryData + [Theory] + [InlineData(85.6f)] + [InlineData(21)] + public void Rotate_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(float angle) { - RotateType.None, - RotateType.Rotate90, - RotateType.Rotate180, - RotateType.Rotate270 - }; + this.operations.Rotate(angle); + var processor = this.Verify>(); + + Assert.Equal(angle, processor.Angle); + Assert.True(processor.Expand); + } + [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(RotateFloatValues), DefaultPixelType)] - public void ImageShouldRotate(TestImageProvider provider, float value) - where TPixel : struct, IPixel + [InlineData(RotateType.None, 0)] + [InlineData(RotateType.Rotate90, 90)] + [InlineData(RotateType.Rotate180, 180)] + [InlineData(RotateType.Rotate270, 270)] + public void Rotate_RotateType_RotateProcessorWithAnglesConvertedFromEnumAndExpandTrue(RotateType angle, float expectedangle) { - using (Image image = provider.GetImage()) - { - image.Rotate(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.Rotate(angle); // is this api needed ??? + var processor = this.Verify>(); + + Assert.Equal(expectedangle, processor.Angle); + Assert.False(processor.Expand); } + [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(RotateEnumValues), DefaultPixelType)] - public void ImageShouldRotateEnum(TestImageProvider provider, RotateType value) - where TPixel : struct, IPixel + [InlineData(85.6f, false)] + [InlineData(21, true)] + [InlineData(21, false)] + public void Rotate_degreesFloat_expand_RotateProcessorWithAnglesSetAndExpandSet(float angle, bool expand) { - using (Image image = provider.GetImage()) - { - image.Rotate(value) - .DebugSave(provider, value, Extensions.Bmp); - } + this.operations.Rotate(angle, expand); + var processor = this.Verify>(); + + Assert.Equal(angle, processor.Angle); + Assert.Equal(expand, processor.Expand); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index f2c2d7cbd..28a0e0d8f 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -1,33 +1,34 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests.Processing.Transforms -{ - using ImageSharp.PixelFormats; - - using Xunit; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; - public class SkewTest : FileTestBase +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class SkewTest : BaseImageOperationsExtensionTest { - public static readonly TheoryData SkewValues - = new TheoryData + [Fact] + public void Skew_x_y_CreateSkewProcessorWithAnglesSetAndExpandTrue() { - { 20, 10 }, - { -20, -10 } - }; + this.operations.Skew(10, 20); + var processor = this.Verify>(); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(SkewValues), DefaultPixelType)] - public void ImageShouldSkew(TestImageProvider provider, float x, float y) - where TPixel : struct, IPixel + Assert.Equal(10, processor.AngleX); + Assert.Equal(20, processor.AngleY); + Assert.True(processor.Expand); + } + + [Fact] + public void Skew_x_y_expand_CreateSkewProcessorWithAnglesSetAndExpandTrue() { - using (Image image = provider.GetImage()) - { - image.Skew(x, y) - .DebugSave(provider, string.Join("_", x, y), Extensions.Bmp); - } + this.operations.Skew(10, 20, false); + var processor = this.Verify>(); + + Assert.Equal(10, processor.AngleX); + Assert.Equal(20, processor.AngleY); + Assert.False(processor.Expand); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestBase.cs b/tests/ImageSharp.Tests/TestBase.cs deleted file mode 100644 index c7514d5ae..000000000 --- a/tests/ImageSharp.Tests/TestBase.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - using System.Reflection; - - using ImageSharp.Formats; - - /// - /// The test base class. Inherit from this class for any image manipulation tests. - /// - public abstract class TestBase - { - /// - /// Creates the image output directory. - /// - /// The path. - /// The path parts. - /// - /// The . - /// - protected string CreateOutputDirectory(string path, params string[] pathParts) - { - string assemblyLocation = typeof(TestFile).GetTypeInfo().Assembly.Location; - assemblyLocation = Path.GetDirectoryName(assemblyLocation); - path = Path.GetFullPath(Path.Combine(assemblyLocation, "../../../TestOutput", path)); - - if (pathParts != null && pathParts.Length > 0) - { - path = Path.Combine(path, Path.Combine(pathParts)); - } - - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - return path; - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs index a14433f1e..4fd798f34 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataArray.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataArray { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs index c8d517aac..b39892a81 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataCurves.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Numerics; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.Tests +{ internal static class IccTestDataCurves { #region Response diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs index cc2dfc6e9..4ab0b0f6b 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataLut.cs @@ -1,9 +1,9 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using SixLabors.ImageSharp.MetaData.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataLut { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs index 78e493829..8e4b7d3d5 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMatrix.cs @@ -1,14 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System.Numerics; +using SixLabors.ImageSharp.Memory; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using ImageSharp.Memory; - internal static class IccTestDataMatrix { #region 2D diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs index 49e9550df..fb648198d 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataMultiProcessElements.cs @@ -1,9 +1,9 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using SixLabors.ImageSharp.MetaData.Profiles.Icc; + +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataMultiProcessElement { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs index fdbcb3127..3b8c3321a 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataNonPrimitives.cs @@ -1,9 +1,13 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Globalization; - using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Globalization; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.Tests +{ internal static class IccTestDataNonPrimitives { #region DateTime diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs index fcfa2d0d7..b24e3f24a 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataPrimitives.cs @@ -1,9 +1,7 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataPrimitives { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 32a4a8e57..3cf66ffed 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -1,12 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System; using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { internal static class IccTestDataProfiles { diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs index 377cf24e7..f7c4efa92 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Globalization; - using System.Numerics; +using System.Globalization; +using System.Numerics; +using SixLabors.ImageSharp.MetaData.Profiles.Icc; +namespace SixLabors.ImageSharp.Tests +{ internal static class IccTestDataTagDataEntry { #region TagDataEntry Header diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 7373b70c5..b736dce20 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -1,19 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - /// /// A test image file. /// @@ -25,19 +24,20 @@ namespace ImageSharp.Tests private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); /// - /// The formats directory, as lazy value + /// The "Formats" directory, as lazy value /// - private static readonly Lazy formatsDirectory = new Lazy(GetFormatsDirectory); + // ReSharper disable once InconsistentNaming + private static readonly Lazy inputImagesDirectory = new Lazy(() => TestEnvironment.InputImagesDirectoryFullPath); /// - /// The image. + /// The image (lazy initialized value) /// - private readonly Image image; + private Image image; /// - /// The file. + /// The image bytes /// - private readonly string file; + private byte[] bytes; /// /// Initializes a new instance of the class. @@ -45,39 +45,40 @@ namespace ImageSharp.Tests /// The file. private TestFile(string file) { - this.file = file; - - this.Bytes = File.ReadAllBytes(file); - this.image = Image.Load(this.Bytes); + this.FullPath = file; } /// - /// Gets the bytes. + /// Gets the image bytes. /// - public byte[] Bytes { get; } + public byte[] Bytes => this.bytes ?? (this.bytes = File.ReadAllBytes(this.FullPath)); /// - /// The file name. + /// The full path to file. /// - public string FilePath => this.file; + public string FullPath { get; } /// /// The file name. /// - public string FileName => Path.GetFileName(this.file); + public string FileName => Path.GetFileName(this.FullPath); /// /// The file name without extension. /// - public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.file); + public string FileNameWithoutExtension => Path.GetFileNameWithoutExtension(this.FullPath); + + /// + /// Gets the image with lazy initialization. + /// + private Image Image => this.image ?? (this.image = ImageSharp.Image.Load(this.Bytes)); /// - /// Gets the "Formats" test file directory. /// - private static string FormatsDirectory => formatsDirectory.Value; + private static string InputImagesDirectory => inputImagesDirectory.Value; /// - /// Gets the full qualified path to the file. + /// Gets the full qualified path to the input test file. /// /// /// The file path. @@ -85,9 +86,9 @@ namespace ImageSharp.Tests /// /// The . /// - public static string GetPath(string file) + public static string GetInputFileFullPath(string file) { - return Path.Combine(FormatsDirectory, file); + return Path.Combine(InputImagesDirectory, file).Replace('\\', Path.DirectorySeparatorChar); } /// @@ -99,7 +100,7 @@ namespace ImageSharp.Tests /// public static TestFile Create(string file) { - return Cache.GetOrAdd(file, (string fileName) => new TestFile(GetPath(file))); + return Cache.GetOrAdd(file, (string fileName) => new TestFile(GetInputFileFullPath(file))); } /// @@ -111,7 +112,7 @@ namespace ImageSharp.Tests /// public string GetFileName(object value) { - return $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.file)}"; + return $"{this.FileNameWithoutExtension}-{value}{Path.GetExtension(this.FullPath)}"; } /// @@ -130,74 +131,22 @@ namespace ImageSharp.Tests /// Creates a new image. /// /// - /// The . + /// The . /// public Image CreateImage() { - return new Image(this.image); + return this.Image.Clone(); } /// /// Creates a new image. /// /// - /// The . + /// The . /// public Image CreateImage(IImageDecoder decoder) { - return Image.Load(this.image.Configuration, this.Bytes, decoder); - } - - /// - /// Gets the correct path to the formats directory. - /// - /// - /// The . - /// - private static string GetFormatsDirectory() - { - List directories = new List< string > { - "TestImages/Formats/", // Here for code coverage tests. - "tests/ImageSharp.Tests/TestImages/Formats/", // from travis/build script - "../../../../../ImageSharp.Tests/TestImages/Formats/", // from Sandbox46 - "../../../../TestImages/Formats/", - "../../../TestImages/Formats/" - }; - - directories = directories.SelectMany(x => new[] - { - Path.GetFullPath(x) - }).ToList(); - - AddFormatsDirectoryFromTestAssebmlyPath(directories); - - string directory = directories.FirstOrDefault(x => Directory.Exists(x)); - - if(directory != null) - { - return directory; - } - - throw new System.Exception($"Unable to find Formats directory at any of these locations [{string.Join(", ", directories)}]"); - } - - /// - /// The path returned by Path.GetFullPath(x) can be relative to dotnet framework directory - /// in certain scenarios like dotTrace test profiling. - /// This method calculates and adds the format directory based on the ImageSharp.Tests assembly location. - /// - /// The directories list - private static void AddFormatsDirectoryFromTestAssebmlyPath(List directories) - { - string assemblyLocation = typeof(TestFile).GetTypeInfo().Assembly.Location; - assemblyLocation = Path.GetDirectoryName(assemblyLocation); - - if (assemblyLocation != null) - { - string dirFromAssemblyLocation = Path.Combine(assemblyLocation, "../../../TestImages/Formats/"); - dirFromAssemblyLocation = Path.GetFullPath(dirFromAssemblyLocation); - directories.Add(dirFromAssemblyLocation); - } + return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); } } } diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs index d43b989f1..304e8dcb8 100644 --- a/tests/ImageSharp.Tests/TestFileSystem.cs +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -1,19 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using ImageSharp.Formats; - using Xunit; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.Formats; +using Xunit; +namespace SixLabors.ImageSharp.Tests +{ /// /// A test image file. /// diff --git a/tests/ImageSharp.Tests/TestFont.cs b/tests/ImageSharp.Tests/TestFont.cs index 7e5b6c370..6f805e367 100644 --- a/tests/ImageSharp.Tests/TestFont.cs +++ b/tests/ImageSharp.Tests/TestFont.cs @@ -1,17 +1,15 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// A test image file. /// diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 5a3cd102e..2683d4752 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -1,21 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; - - using Xunit; - /// /// A test image file. /// diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 764d768ae..dbcacb4f3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System.Linq; +// ReSharper disable InconsistentNaming // ReSharper disable MemberHidesStaticFromOuterClass -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System.Linq; - /// /// Class that contains all the relative test image paths in the TestImages/Formats directory. /// Use with , or . @@ -26,7 +25,12 @@ namespace ImageSharp.Tests public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; public const string Rgb48Bpp = "Png/rgb-48bpp.png"; + public const string CalliphoraPartial = "Png/CalliphoraPartial.png"; + public const string CalliphoraPartialGrayscale = "Png/CalliphoraPartialGrayscale.png"; + public const string Bike = "Png/Bike.png"; + public const string BikeGrayscale = "Png/BikeGrayscale.png"; public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png"; + public const string SnakeGame = "Png/SnakeGame.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; @@ -44,6 +48,9 @@ namespace ImageSharp.Tests public const string VersioningImage1 = "Png/versioning-1_1.png"; public const string VersioningImage2 = "Png/versioning-1_2.png"; + public const string Banner7Adam7InterlaceMode = "Png/banner7-adam.png"; + public const string Banner8Index = "Png/banner8-index.png"; + public static class Bad { // Odd chunk lengths @@ -81,7 +88,7 @@ namespace ImageSharp.Tests { public static class Bad { - public const string MissingEOF = "Jpg/baseline/badeof.jpg"; + public const string BadEOF = "Jpg/baseline/badeof.jpg"; public const string ExifUndefType = "Jpg/baseline/ExifUndefType.jpg"; } @@ -96,18 +103,26 @@ namespace ImageSharp.Tests public const string Snake = "Jpg/baseline/Snake.jpg"; public const string Lake = "Jpg/baseline/Lake.jpg"; public const string Jpeg400 = "Jpg/baseline/jpeg400jfif.jpg"; - public const string Jpeg420 = "Jpg/baseline/jpeg420exif.jpg"; + public const string Jpeg420Exif = "Jpg/baseline/jpeg420exif.jpg"; public const string Jpeg444 = "Jpg/baseline/jpeg444.jpg"; - public const string Testimgorig = "Jpg/baseline/testorig.jpg"; + public const string Jpeg420Small = "Jpg/baseline/jpeg420small.jpg"; + public const string Testorig420 = "Jpg/baseline/testorig.jpg"; public static readonly string[] All = { Cmyk, Ycck, Exif, Floorplan, Calliphora, Turtle, GammaDalaiLamaGray, - Hiyamugi, Jpeg400, Jpeg420, Jpeg444, + Hiyamugi, Jpeg400, Jpeg420Exif, Jpeg444, }; } + public class Issues + { + public const string CriticalEOF214 = "Jpg/issues/Issue214-CriticalEOF.jpg"; + public const string MissingFF00ProgressiveGirl159 = "Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg"; + public const string BadCoeffsProgressive178 = "Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg"; + } + public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 333b645de..6b3f6ccd2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -1,9 +1,12 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +namespace SixLabors.ImageSharp.Tests +{ internal struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer { private readonly float Eps; diff --git a/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs b/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs index d3b4da9b9..e342f7029 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ArrayHelper.cs @@ -1,12 +1,10 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System.Linq; +using System.Linq; +namespace SixLabors.ImageSharp.Tests +{ public static class ArrayHelper { /// diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index 05e8c6136..ec3254921 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -1,17 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - - using Xunit.Sdk; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Xunit.Sdk; +namespace SixLabors.ImageSharp.Tests +{ /// /// Base class for Theory Data attributes which pass an instance of to the test case. /// @@ -54,7 +51,7 @@ namespace ImageSharp.Tests if (!string.IsNullOrWhiteSpace(this.MemberName)) { Type type = this.MemberType ?? testMethod.DeclaringType; - Func accessor = this.GetPropertyAccessor(type) ?? this.GetFieldAccessor(type); + Func accessor = this.GetPropertyAccessor(type, this.MemberName) ?? this.GetFieldAccessor(type, this.MemberName); if (accessor != null) { @@ -156,12 +153,12 @@ namespace ImageSharp.Tests /// /// Gets the field accessor for the given type. /// - Func GetFieldAccessor(Type type) + protected Func GetFieldAccessor(Type type, string memberName) { FieldInfo fieldInfo = null; for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - fieldInfo = reflectionType.GetRuntimeField(this.MemberName); + fieldInfo = reflectionType.GetRuntimeField(memberName); if (fieldInfo != null) break; } @@ -175,12 +172,12 @@ namespace ImageSharp.Tests /// /// Gets the property accessor for the given type. /// - Func GetPropertyAccessor(Type type) + protected Func GetPropertyAccessor(Type type, string memberName) { PropertyInfo propInfo = null; for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - propInfo = reflectionType.GetRuntimeProperty(this.MemberName); + propInfo = reflectionType.GetRuntimeProperty(memberName); if (propInfo != null) { break; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs index 72be5abc2..796cba855 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which produce a blank image of size width * height. /// One instance will be passed for each the pixel format defined by the pixelTypes parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs index ec8421853..e896c1854 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which read an image from the given file /// One instance will be passed for each the pixel format defined by the pixelTypes parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs index 1b37c45a9..170251389 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs @@ -1,15 +1,13 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName /// instances will be passed for each the pixel format defined by the pixelTypes parameter @@ -38,18 +36,18 @@ namespace ImageSharp.Tests /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName /// instances will be passed for each the pixel format defined by the pixelTypes parameter /// - /// The name of the static test class field/property enumerating the files + /// The name of the static test class field/property enumerating the files /// The member name for enumerating method parameters /// The requested pixel types /// Additional theory parameter values public WithFileCollectionAttribute( - string enumeratorMemberName, + string fileEnumeratorMemberName, string memberName, PixelTypes pixelTypes, params object[] additionalParameters) : base(memberName, pixelTypes, additionalParameters) { - this.fileEnumeratorMemberName = enumeratorMemberName; + this.fileEnumeratorMemberName = fileEnumeratorMemberName; } /// @@ -60,8 +58,8 @@ namespace ImageSharp.Tests /// The protected override IEnumerable GetAllFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { - Func accessor = this.GetPropertyAccessor(testMethod.DeclaringType); - accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType); + Func accessor = this.GetPropertyAccessor(testMethod.DeclaringType, this.fileEnumeratorMemberName); + accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType, this.fileEnumeratorMemberName); var files = (IEnumerable)accessor(); return files.Select(f => new object[] { f }); @@ -69,52 +67,5 @@ namespace ImageSharp.Tests /// protected override string GetFactoryMethodName(MethodInfo testMethod) => "File"; - - /// - /// Gets the field accessor for the given type. - /// - private Func GetFieldAccessor(Type type) - { - FieldInfo fieldInfo = null; - for (Type reflectionType = type; - reflectionType != null; - reflectionType = reflectionType.GetTypeInfo().BaseType) - { - fieldInfo = reflectionType.GetRuntimeField(this.fileEnumeratorMemberName); - if (fieldInfo != null) - { - break; - } - } - - if (fieldInfo == null || !fieldInfo.IsStatic) - { - return null; - } - - return () => fieldInfo.GetValue(null); - } - - /// - /// Gets the property accessor for the given type. - /// - private Func GetPropertyAccessor(Type type) - { - PropertyInfo propInfo = null; - for (Type reflectionType = type; - reflectionType != null; - reflectionType = reflectionType.GetTypeInfo().BaseType) - { - propInfo = reflectionType.GetRuntimeProperty(this.fileEnumeratorMemberName); - if (propInfo != null) break; - } - - if (propInfo?.GetMethod == null || !propInfo.GetMethod.IsStatic) - { - return null; - } - - return () => propInfo.GetValue(null, null); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index 6c6198c38..cdf5fcb08 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Linq; - using System.Reflection; +using System; +using System.Linq; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which return the image produced by the given test class member method /// instances will be passed for each the pixel format defined by the pixelTypes parameter @@ -39,9 +37,8 @@ namespace ImageSharp.Tests Type colorType = args.Single(); Type imgType = typeof(Image<>).MakeGenericType(colorType); - Type genericFactoryType = (typeof(GenericFactory<>)).MakeGenericType(colorType); - Type funcType = typeof(Func<,>).MakeGenericType(genericFactoryType, imgType); + Type funcType = typeof(Func<>).MakeGenericType(imgType); MethodInfo genericMethod = m.MakeGenericMethod(args); diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs index 9a8538e78..f787a3591 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which produce an image of size width * height filled with the requested color. /// One instance will be passed for each the pixel format defined by the pixelTypes parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs index f6ab65f71..585bb8f06 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; +namespace SixLabors.ImageSharp.Tests +{ /// /// Triggers passing instances which produce a blank image of size width * height. /// One instance will be passed for each the pixel format defined by the pixelTypes parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs deleted file mode 100644 index 4a0950788..000000000 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System; - - using ImageSharp.PixelFormats; - - /// - /// Utility class to create specialized subclasses of generic classes (eg. ) - /// Used as parameter for -based factory methods - /// - public class GenericFactory - where TPixel : struct, IPixel - { - public virtual Image CreateImage(int width, int height) - { - return new Image(width, height); - } - - public virtual Image CreateImage(byte[] bytes) - { - return Image.Load(bytes); - } - - public virtual Image CreateImage(Image other) - { - return new Image(other); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs deleted file mode 100644 index 20af430a5..000000000 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using ImageSharp.PixelFormats; - - public class ImageFactory : GenericFactory - { - public override Image CreateImage(byte[] bytes) => Image.Load(bytes); - - public override Image CreateImage(int width, int height) => new Image(width, height); - - public override Image CreateImage(Image other) - { - return new Image(other); - } - } -} diff --git a/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs b/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs index dd3ef3a4f..f7732fce5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs @@ -1,9 +1,12 @@ -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Numerics; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +namespace SixLabors.ImageSharp.Tests +{ /// /// Allows the comparison of single-precision floating point values by precision. /// diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs new file mode 100644 index 000000000..dbe252366 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -0,0 +1,57 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.PixelFormats; + + using SixLabors.Primitives; + + public class ExactImageComparer : ImageComparer + { + public static ExactImageComparer Instance { get; } = new ExactImageComparer(); + + public override ImageSimilarityReport CompareImagesOrFrames( + ImageFrame expected, + ImageFrame actual) + { + if (expected.Size() != actual.Size()) + { + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); + } + + int width = actual.Width; + + // TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types. + + Rgba32[] aBuffer = new Rgba32[width]; + Rgba32[] bBuffer = new Rgba32[width]; + + var differences = new List(); + + for (int y = 0; y < actual.Height; y++) + { + Span aSpan = expected.GetPixelRowSpan(y); + Span bSpan = actual.GetPixelRowSpan(y); + + PixelOperations.Instance.ToRgba32(aSpan, aBuffer, width); + PixelOperations.Instance.ToRgba32(bSpan, bBuffer, width); + + for (int x = 0; x < width; x++) + { + Rgba32 aPixel = aBuffer[x]; + Rgba32 bPixel = bBuffer[x]; + + if (aPixel != bPixel) + { + var diff = new PixelDifference(new Point(x, y), aPixel, bPixel); + differences.Add(diff); + } + } + } + + return new ImageSimilarityReport(expected, actual, differences); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs new file mode 100644 index 000000000..e5f031b50 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs @@ -0,0 +1,34 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + public class ImageDifferenceIsOverThresholdException : ImagesSimilarityException + { + public ImageSimilarityReport[] Reports { get; } + + public ImageDifferenceIsOverThresholdException(IEnumerable reports) + : base("Image difference is over threshold!" + StringifyReports(reports)) + { + this.Reports = reports.ToArray(); + } + + private static string StringifyReports(IEnumerable reports) + { + StringBuilder sb = new StringBuilder(); + sb.Append(Environment.NewLine); + + int i = 0; + foreach (ImageSimilarityReport r in reports) + { + sb.Append($"Report{i}: "); + sb.Append(r); + sb.Append(Environment.NewLine); + i++; + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs new file mode 100644 index 000000000..e8f60ade3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDimensionsMismatchException.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + public class ImageDimensionsMismatchException : ImagesSimilarityException + { + public ImageDimensionsMismatchException(Size expectedSize, Size actualSize) + : base((string)$"The image dimensions {actualSize} do not match the expected {expectedSize}!") + { + this.ExpectedSize = expectedSize; + this.ActualSize = actualSize; + } + + public Size ExpectedSize { get; } + public Size ActualSize { get; } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs new file mode 100644 index 000000000..bbdb6b581 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImagesSimilarityException.cs @@ -0,0 +1,12 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + + public class ImagesSimilarityException : Exception + { + public ImagesSimilarityException(string message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs new file mode 100644 index 000000000..829bf3d10 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -0,0 +1,131 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using SixLabors.ImageSharp.Helpers; +using SixLabors.ImageSharp.PixelFormats; + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + public abstract class ImageComparer + { + public static ImageComparer Exact { get; } = Tolerant(0, 0); + + public static ImageComparer Tolerant( + float imageThreshold = TolerantImageComparer.DefaultImageThreshold, + int perPixelManhattanThreshold = 0) + { + return new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); + } + + public abstract ImageSimilarityReport CompareImagesOrFrames( + ImageFrame expected, + ImageFrame actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel; + } + + public static class ImageComparerExtensions + { + public static ImageSimilarityReport CompareImagesOrFrames( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + return comparer.CompareImagesOrFrames((ImageFrame)expected, (ImageFrame)actual); + } + + public static IEnumerable> CompareImages( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + var result = new List>(); + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new Exception("Frame count does not match!"); + } + for (int i = 0; i < expected.Frames.Count; i++) + { + ImageSimilarityReport report = comparer.CompareImagesOrFrames(expected.Frames[i], actual.Frames[i]); + if (!report.IsEmpty) + { + result.Add(report); + } + } + + return result; + } + + public static void VerifySimilarity( + this ImageComparer comparer, + Image expected, + Image actual) + where TPixelA : struct, IPixel where TPixelB : struct, IPixel + { + if (expected.Size() != actual.Size()) + { + throw new ImageDimensionsMismatchException(expected.Size(), actual.Size()); + } + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new ImagesSimilarityException("Image frame count does not match!"); + } + + IEnumerable reports = comparer.CompareImages(expected, actual); + if (reports.Any()) + { + throw new ImageDifferenceIsOverThresholdException(reports); + } + } + + public static void VerifySimilarityIgnoreRegion( + this ImageComparer comparer, + Image expected, + Image actual, + Rectangle ignoredRegion) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel + { + if (expected.Size() != actual.Size()) + { + throw new ImageDimensionsMismatchException(expected.Size(), actual.Size()); + } + + if (expected.Frames.Count != actual.Frames.Count) + { + throw new ImagesSimilarityException("Image frame count does not match!"); + } + + IEnumerable> reports = comparer.CompareImages(expected, actual); + if (reports.Any()) + { + List> cleanedReports = new List>(reports.Count()); + foreach (var r in reports) + { + var outsideChanges = r.Differences.Where(x => !( + ignoredRegion.X <= x.Position.X && + x.Position.X <= ignoredRegion.Right && + ignoredRegion.Y <= x.Position.Y && + x.Position.Y <= ignoredRegion.Bottom)); + if (outsideChanges.Any()) + { + cleanedReports.Add(new ImageSimilarityReport(r.ExpectedImage, r.ActualImage, outsideChanges, null)); + } + } + + if (cleanedReports.Any()) + { + throw new ImageDifferenceIsOverThresholdException(cleanedReports); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs new file mode 100644 index 000000000..78e390bbd --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageSimilarityReport.cs @@ -0,0 +1,87 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using SixLabors.ImageSharp.PixelFormats; + + public class ImageSimilarityReport + { + protected ImageSimilarityReport( + object expectedImage, + object actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) + { + this.ExpectedImage = expectedImage; + this.ActualImage = actualImage; + this.TotalNormalizedDifference = totalNormalizedDifference; + this.Differences = differences.ToArray(); + } + public object ExpectedImage { get; } + + public object ActualImage { get; } + + // TODO: This should not be a nullable value! + public float? TotalNormalizedDifference { get; } + + public string DifferencePercentageString => this.TotalNormalizedDifference.HasValue + ? $"{this.TotalNormalizedDifference.Value * 100:0.0000}%" + : "?"; + + public PixelDifference[] Differences { get; } + + public bool IsEmpty => this.Differences.Length == 0; + + public override string ToString() + { + return this.IsEmpty ? "[SimilarImages]" : this.PrintDifference(); + } + + private string PrintDifference() + { + var sb = new StringBuilder(); + if (this.TotalNormalizedDifference.HasValue) + { + sb.AppendLine($"Total difference: {this.DifferencePercentageString}"); + } + int max = Math.Min(5, this.Differences.Length); + + for (int i = 0; i < max; i++) + { + sb.Append(this.Differences[i]); + if (i < max - 1) + { + sb.Append("; "); + } + } + if (this.Differences.Length >= 5) + { + sb.Append("..."); + } + return sb.ToString(); + } + } + + public class ImageSimilarityReport : ImageSimilarityReport + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel + { + public ImageSimilarityReport( + ImageFrame expectedImage, + ImageFrame actualImage, + IEnumerable differences, + float? totalNormalizedDifference = null) + : base(expectedImage, actualImage, differences, totalNormalizedDifference) + { + } + + public static ImageSimilarityReport Empty => + new ImageSimilarityReport(null, null, Enumerable.Empty(), 0f); + + public new ImageFrame ExpectedImage => (ImageFrame)base.ExpectedImage; + + public new ImageFrame ActualImage => (ImageFrame)base.ActualImage; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs new file mode 100644 index 000000000..fcfb31d68 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/PixelDifference.cs @@ -0,0 +1,40 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using SixLabors.Primitives; + + public struct PixelDifference + { + public PixelDifference( + Point position, + int redDifference, + int greenDifference, + int blueDifference, + int alphaDifference) + { + this.Position = position; + this.RedDifference = redDifference; + this.GreenDifference = greenDifference; + this.BlueDifference = blueDifference; + this.AlphaDifference = alphaDifference; + } + + public PixelDifference(Point position, Rgba32 expected, Rgba32 actual) + : this(position, + (int)actual.R - (int)expected.R, + (int)actual.G - (int)expected.G, + (int)actual.B - (int)expected.B, + (int)actual.A - (int)expected.A) + { + } + + public Point Position { get; } + + public int RedDifference { get; } + public int GreenDifference { get; } + public int BlueDifference { get; } + public int AlphaDifference { get; } + + public override string ToString() => + $"[Δ({this.RedDifference},{this.GreenDifference},{this.BlueDifference},{this.AlphaDifference}) @ ({this.Position.X},{this.Position.Y})]"; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs new file mode 100644 index 000000000..d20bd72ac --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -0,0 +1,111 @@ +namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison +{ + using System; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Helpers; + using SixLabors.ImageSharp.PixelFormats; + + using SixLabors.Primitives; + + public class TolerantImageComparer : ImageComparer + { + public const float DefaultImageThreshold = 1.0f / (100 * 100 * 255); + + public TolerantImageComparer(float imageThreshold, int perPixelManhattanThreshold = 0) + { + this.ImageThreshold = imageThreshold; + this.PerPixelManhattanThreshold = perPixelManhattanThreshold; + } + + /// + /// The maximal tolerated difference represented by a value between 0.0 and 1.0. + /// Examples of percentage differences on a single pixel: + /// 1. PixelA = (255,255,255,0) PixelB =(0,0,0,255) leads to 100% difference on a single pixel + /// 2. PixelA = (255,255,255,0) PixelB =(255,255,255,255) leads to 25% difference on a single pixel + /// 3. PixelA = (255,255,255,0) PixelB =(128,128,128,128) leads to 50% difference on a single pixel + /// + /// The total differences is the sum of all pixel differences normalized by image dimensions! + /// The individual distances are calculated using the Manhattan function: + /// + /// https://en.wikipedia.org/wiki/Taxicab_geometry + /// + /// ImageThresholdInPercents = 1.0/255 means that we allow one byte difference per channel on a 1x1 image + /// ImageThresholdInPercents = 1.0/(100*100*255) means that we allow only one byte difference per channel on a 100x100 image + /// + public float ImageThreshold { get; } + + /// + /// The threshold of the individual pixels before they acumulate towards the overall difference. + /// For an individual pixel pair the value is the Manhattan distance of pixels: + /// + /// https://en.wikipedia.org/wiki/Taxicab_geometry + /// + /// + public int PerPixelManhattanThreshold { get; } + + public override ImageSimilarityReport CompareImagesOrFrames(ImageFrame expected, ImageFrame actual) + { + if (expected.Size() != actual.Size()) + { + throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); + } + + int width = actual.Width; + + // TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types. + + Rgba32[] aBuffer = new Rgba32[width]; + Rgba32[] bBuffer = new Rgba32[width]; + + float totalDifference = 0.0f; + + var differences = new List(); + + for (int y = 0; y < actual.Height; y++) + { + Span aSpan = expected.GetPixelRowSpan(y); + Span bSpan = actual.GetPixelRowSpan(y); + + PixelOperations.Instance.ToRgba32(aSpan, aBuffer, width); + PixelOperations.Instance.ToRgba32(bSpan, bBuffer, width); + + for (int x = 0; x < width; x++) + { + int d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]); + + if (d > this.PerPixelManhattanThreshold) + { + var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]); + differences.Add(diff); + + totalDifference += d; + } + } + } + + float normalizedDifference = totalDifference / ((float)actual.Width * (float)actual.Height); + normalizedDifference /= 4.0f * 255.0f; + + if (normalizedDifference > this.ImageThreshold) + { + return new ImageSimilarityReport(expected, actual, differences, normalizedDifference); + } + else + { + return ImageSimilarityReport.Empty; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetManhattanDistanceInRgbaSpace(ref Rgba32 a, ref Rgba32 b) + { + return Diff(a.R, b.R) + Diff(a.G, b.G) + Diff(a.B, b.B) + Diff(a.A, b.A); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Diff(byte a, byte b) => Math.Abs(a - b); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 4252a60b5..78821aac1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public abstract partial class TestImageProvider where TPixel : struct, IPixel { @@ -33,7 +31,7 @@ namespace ImageSharp.Tests protected int Width { get; private set; } - public override Image GetImage() => this.Factory.CreateImage(this.Width, this.Height); + public override Image GetImage() => new Image(this.Width, this.Height); public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 16eecd2fc..8ff532b2f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public abstract partial class TestImageProvider where TPixel : struct, IPixel { @@ -19,47 +20,130 @@ namespace ImageSharp.Tests { // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider // are shared between PixelTypes.Color & PixelTypes.Rgba32 - private class Key : Tuple + private class Key : IEquatable { - public Key(PixelTypes item1, string item2) - : base(item1, item2) + private Tuple commonValues; + + private Dictionary decoderParameters; + + public Key(PixelTypes pixelType, string filePath, IImageDecoder customDecoder) + { + Type customType = customDecoder?.GetType(); + this.commonValues = new Tuple(pixelType, filePath, customType); + this.decoderParameters = GetDecoderParameters(customDecoder); + } + + private static Dictionary GetDecoderParameters(IImageDecoder customDecoder) + { + Type type = customDecoder.GetType(); + + var data = new Dictionary(); + + while (type != null && type != typeof(object)) + { + PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + { + string key = $"{type.FullName}.{p.Name}"; + object value = p.GetValue(customDecoder); + data[key] = value; + } + type = type.GetTypeInfo().BaseType; + } + return data; + } + + public bool Equals(Key other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!this.commonValues.Equals(other.commonValues)) return false; + + if (this.decoderParameters.Count != other.decoderParameters.Count) + { + return false; + } + + foreach (KeyValuePair kv in this.decoderParameters) + { + object otherVal; + if (!other.decoderParameters.TryGetValue(kv.Key, out otherVal)) + { + return false; + } + if (!object.Equals(kv.Value, otherVal)) + { + return false; + } + } + return true; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return this.Equals((Key)obj); + } + + public override int GetHashCode() + { + return this.commonValues.GetHashCode(); + } + + public static bool operator ==(Key left, Key right) + { + return Equals(left, right); + } + + public static bool operator !=(Key left, Key right) { + return !Equals(left, right); } } private static readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); - private string filePath; - public FileProvider(string filePath) { - this.filePath = filePath; + this.FilePath = filePath; } public FileProvider() { } - public override string SourceFileOrDescription => this.filePath; + public string FilePath { get; private set; } + + public override string SourceFileOrDescription => this.FilePath; public override Image GetImage() { - Key key = new Key(this.PixelType, this.filePath); + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(this.FilePath); + return this.GetImage(decoder); + } + + public override Image GetImage(IImageDecoder decoder) + { + Guard.NotNull(decoder, nameof(decoder)); + + Key key = new Key(this.PixelType, this.FilePath, decoder); Image cachedImage = cache.GetOrAdd( key, fn => { - TestFile testFile = TestFile.Create(this.filePath); - return this.Factory.CreateImage(testFile.Bytes); + TestFile testFile = TestFile.Create(this.FilePath); + return Image.Load(testFile.Bytes, decoder); }); - return this.Factory.CreateImage(cachedImage); + return cachedImage.Clone(); } public override void Deserialize(IXunitSerializationInfo info) { - this.filePath = info.GetValue("path"); + this.FilePath = info.GetValue("path"); base.Deserialize(info); // must be called last } @@ -67,8 +151,14 @@ namespace ImageSharp.Tests public override void Serialize(IXunitSerializationInfo info) { base.Serialize(info); - info.AddValue("path", this.filePath); + info.AddValue("path", this.FilePath); } } + + public static string GetFilePathOrNull(ITestImageProvider provider) + { + var fileProvider = provider as FileProvider; + return fileProvider?.FilePath; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs index 30e7a63b5..5bd53a4c0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs @@ -1,14 +1,12 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ /// /// Provides instances for parametric unit tests. /// @@ -18,14 +16,14 @@ namespace ImageSharp.Tests { private class LambdaProvider : TestImageProvider { - private readonly Func, Image> creator; + private readonly Func> factoryFunc; - public LambdaProvider(Func, Image> creator) + public LambdaProvider(Func> factoryFunc) { - this.creator = creator; + this.factoryFunc = factoryFunc; } - public override Image GetImage() => this.creator(this.Factory); + public override Image GetImage() => this.factoryFunc(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 0caded420..32860d2a4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -1,16 +1,14 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ /// /// Provides instances for parametric unit tests. /// @@ -18,7 +16,7 @@ namespace ImageSharp.Tests public abstract partial class TestImageProvider where TPixel : struct, IPixel { - private class SolidProvider : BlankProvider + private class SolidProvider : BlankProvider { private byte a; @@ -55,9 +53,10 @@ namespace ImageSharp.Tests TPixel color = default(TPixel); color.PackFromRgba32(new Rgba32(this.r, this.g, this.b, this.a)); - return image.Fill(color); + image.Mutate(x => x.Fill(color)); + return image; } - + public override void Serialize(IXunitSerializationInfo info) { info.AddValue("red", this.r); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 03a685d34..91bbd32ef 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -1,16 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Reflection; +using System; +using System.Reflection; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; - using ImageSharp.PixelFormats; +using Xunit.Abstractions; - using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public interface ITestImageProvider { PixelTypes PixelType { get; } @@ -32,7 +32,6 @@ namespace ImageSharp.Tests /// public ImagingTestCaseUtility Utility { get; private set; } - public GenericFactory Factory { get; private set; } = new GenericFactory(); public string TypeName { get; private set; } public string MethodName { get; private set; } @@ -59,10 +58,10 @@ namespace ImageSharp.Tests } public static TestImageProvider Lambda( - Func, Image> func, + Func> factoryFunc, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - => new LambdaProvider(func).Init(testMethod, pixelTypeOverride); + => new LambdaProvider(factoryFunc).Init(testMethod, pixelTypeOverride); public static TestImageProvider Solid( int width, @@ -82,6 +81,21 @@ namespace ImageSharp.Tests /// public abstract Image GetImage(); + public virtual Image GetImage(IImageDecoder decoder) + { + throw new NotSupportedException($"Decoder specific GetImage() is not supported with {this.GetType().Name}!"); + } + + /// + /// Returns an instance to the test case with the necessary traits. + /// + public Image GetImage(Action> operationsToApply) + { + var img = GetImage(); + img.Mutate(operationsToApply); + return img; + } + public virtual void Deserialize(IXunitSerializationInfo info) { PixelTypes pixelType = info.GetValue("PixelType"); @@ -106,12 +120,7 @@ namespace ImageSharp.Tests } this.TypeName = typeName; this.MethodName = methodName; - - if (pixelTypeOverride == PixelTypes.Rgba32) - { - this.Factory = new ImageFactory() as GenericFactory; - } - + this.Utility = new ImagingTestCaseUtility { SourceFileOrDescription = this.SourceFileOrDescription, diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 96d38fc40..6cee83566 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Numerics; +using System; +using System.Collections.Generic; +using System.Numerics; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; - using Xunit.Abstractions; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public abstract partial class TestImageProvider where TPixel : struct, IPixel { @@ -49,7 +47,7 @@ namespace ImageSharp.Tests } } - return new Image(testImages[this.SourceFileOrDescription]); + return testImages[this.SourceFileOrDescription].Clone(); } /// @@ -62,7 +60,7 @@ namespace ImageSharp.Tests using (PixelAccessor pixels = image.Lock()) { BlackWhiteChecker(pixels); // top left - VirticalBars(pixels); // top right + VerticalBars(pixels); // top right TransparentGradients(pixels); // bottom left Rainbow(pixels); // bottom right } @@ -71,7 +69,7 @@ namespace ImageSharp.Tests /// Fills the top right quadrant with alternating solid vertical bars. /// /// - private static void VirticalBars(PixelAccessor pixels) + private static void VerticalBars(PixelAccessor pixels) { // topLeft int left = pixels.Width / 2; @@ -80,9 +78,9 @@ namespace ImageSharp.Tests int bottom = pixels.Height / 2; int stride = pixels.Width / 12; TPixel[] c = { - NamedColors.HotPink, - NamedColors.Blue - }; + NamedColors.HotPink, + NamedColors.Blue + }; int p = 0; for (int y = top; y < bottom; y++) { @@ -111,9 +109,9 @@ namespace ImageSharp.Tests int bottom = pixels.Height / 2; int stride = pixels.Width / 6; TPixel[] c = { - NamedColors.Black, - NamedColors.White - }; + NamedColors.Black, + NamedColors.White + }; int p = 0; for (int y = top; y < bottom; y++) @@ -199,14 +197,14 @@ namespace ImageSharp.Tests Rgba32 t = new Rgba32(0); for (int x = left; x < right; x++) - for (int y = top; y < bottom; y++) - { - t.PackedValue += stepsPerPixel; - Vector4 v = t.ToVector4(); - //v.W = (x - left) / (float)left; - c.PackFromVector4(v); - pixels[x, y] = c; - } + for (int y = top; y < bottom; y++) + { + t.PackedValue += stepsPerPixel; + Vector4 v = t.ToVector4(); + //v.W = (x - left) / (float)left; + c.PackFromVector4(v); + pixels[x, y] = c; + } } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 47085cf5f..f4faa9456 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -1,23 +1,22 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; - using ImageSharp.Formats; - using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ /// /// Utility class to provide information about the test image & the test case for the test code, /// and help managing IO. /// - public class ImagingTestCaseUtility : TestBase + public class ImagingTestCaseUtility { /// /// Name of the TPixel in the owner @@ -40,12 +39,7 @@ namespace ImageSharp.Tests /// public string TestName { get; set; } = string.Empty; - /// - /// Gets the recommended file name for the output of the test - /// - /// - /// The required extension - public string GetTestOutputFileName(string extension = null, string tag = null) + private string GetTestOutputFileNameImpl(string extension, string details, bool appendPixelTypeToFileName) { string fn = string.Empty; @@ -65,6 +59,7 @@ namespace ImageSharp.Tests { extension = ".bmp"; } + extension = extension.ToLower(); if (extension[0] != '.') { @@ -73,22 +68,62 @@ namespace ImageSharp.Tests if (fn != string.Empty) fn = '_' + fn; - string pixName = this.PixelTypeName; - if (pixName != string.Empty) + string pixName = ""; + + if (appendPixelTypeToFileName) { - pixName = '_' + pixName; + pixName = this.PixelTypeName; + + if (pixName != string.Empty) + { + pixName = '_' + pixName; + } } - tag = tag ?? string.Empty; - if (tag != string.Empty) + details = details ?? string.Empty; + if (details != string.Empty) { - tag = '_' + tag; + details = '_' + details; } + return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{details}{extension}"; + } + + /// + /// Gets the recommended file name for the output of the test + /// + /// The required extension + /// The settings modifying the output path + /// A boolean indicating whether to append the pixel type to output file name. + /// The file test name + public string GetTestOutputFileName(string extension = null, object testOutputDetails = null, bool appendPixelTypeToFileName = true) + { + string detailsString = null; + string s = testOutputDetails as string; - return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{tag}{extension}"; + if (s != null) + { + detailsString = s; + } + else if (testOutputDetails != null) + { + Type type = testOutputDetails.GetType(); + TypeInfo info = type.GetTypeInfo(); + if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) + { + detailsString = testOutputDetails.ToString(); + } + else + { + IEnumerable properties = testOutputDetails.GetType().GetRuntimeProperties(); + + detailsString = String.Join("_", properties.ToDictionary(x => x.Name, x => x.GetValue(testOutputDetails)).Select(x => $"{x.Key}-{x.Value}")); + } + } + return this.GetTestOutputFileNameImpl(extension, detailsString, appendPixelTypeToFileName); } + /// /// Encodes image by the format matching the required extension, than saves it to the recommended output file. /// @@ -96,18 +131,32 @@ namespace ImageSharp.Tests /// The image instance /// The requested extension /// Optional encoder - /// Optional encoder options - public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, string tag = null) + public string SaveTestOutputFile( + Image image, + string extension = null, + IImageEncoder encoder = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) where TPixel : struct, IPixel { - string path = this.GetTestOutputFileName(extension: extension, tag:tag); - extension = Path.GetExtension(path); - encoder = encoder ?? GetImageFormatByExtension(extension); + string path = this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + encoder = encoder ?? TestEnvironment.GetReferenceEncoder(path); using (FileStream stream = File.OpenWrite(path)) { image.Save(stream, encoder); } + return path; + } + + internal string GetReferenceOutputFileName( + string extension, + object settings, + bool appendPixelTypeToFileName) + { + return TestEnvironment.GetReferenceOutputFileName( + this.GetTestOutputFileName(extension, settings, appendPixelTypeToFileName) + ); } internal void Init(string typeName, string methodName) @@ -121,17 +170,84 @@ namespace ImageSharp.Tests this.Init(method.DeclaringType.Name, method.Name); } - private static IImageEncoder GetImageFormatByExtension(string extension) + //private static IImageEncoder GetEncoderByExtension(string extension, bool grayscale) + //{ + // extension = extension?.TrimStart('.'); + // var format = Configuration.Default.FindFormatByFileExtension(extension); + // IImageEncoder encoder = Configuration.Default.FindEncoder(format); + // PngEncoder pngEncoder = encoder as PngEncoder; + // if (pngEncoder != null) + // { + // pngEncoder = new PngEncoder(); + // encoder = pngEncoder; + // pngEncoder.CompressionLevel = 9; + + // if (grayscale) + // { + // pngEncoder.PngColorType = PngColorType.Grayscale; + // } + // } + + // return encoder; + //} + + internal string GetTestOutputDir() { - extension = extension?.TrimStart('.'); - var format = Configuration.Default.FindFormatByFileExtensions(extension); - return Configuration.Default.FindEncoder(format); + string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); + return TestEnvironment.CreateOutputDirectory(testGroupName); } - private string GetTestOutputDir() + public static void ModifyPixel(Image img, int x, int y, byte perChannelChange) + where TPixel : struct, IPixel { - string testGroupName = Path.GetFileNameWithoutExtension(this.TestGroupName); - return CreateOutputDirectory(testGroupName); + ModifyPixel((ImageFrame)img, x, y, perChannelChange); + } + + public static void ModifyPixel(ImageFrame img, int x, int y, byte perChannelChange) + where TPixel : struct, IPixel + { + TPixel pixel = img[x, y]; + var rgbaPixel = default(Rgba32); + pixel.ToRgba32(ref rgbaPixel); + + if (rgbaPixel.R + perChannelChange <= 255) + { + rgbaPixel.R += perChannelChange; + } + else + { + rgbaPixel.R -= perChannelChange; + } + + if (rgbaPixel.G + perChannelChange <= 255) + { + rgbaPixel.G += perChannelChange; + } + else + { + rgbaPixel.G -= perChannelChange; + } + + if (rgbaPixel.B + perChannelChange <= 255) + { + rgbaPixel.B += perChannelChange; + } + else + { + rgbaPixel.B -= perChannelChange; + } + + if (rgbaPixel.A + perChannelChange <= 255) + { + rgbaPixel.A += perChannelChange; + } + else + { + rgbaPixel.A -= perChannelChange; + } + + pixel.PackFromRgba32(rgbaPixel); + img[x, y] = pixel; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs index 1a6bf5d8b..c892c09de 100644 --- a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs +++ b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs @@ -1,20 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - - using Xunit.Abstractions; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ /// /// Utility class to measure the execution of an operation. It can be used either by inheritance or by composition. /// - public class MeasureFixture : TestBase + public class MeasureFixture { /// /// Value indicating whether priniting is enabled. @@ -59,4 +56,27 @@ namespace ImageSharp.Tests protected ITestOutputHelper Output { get; } } + + public class MeasureGuard : IDisposable + { + private readonly string operation; + + private readonly Stopwatch stopwatch = new Stopwatch(); + + public MeasureGuard(ITestOutputHelper output, string operation) + { + this.operation = operation; + this.Output = output; + this.Output.WriteLine(operation + " ..."); + this.stopwatch.Start(); + } + + private ITestOutputHelper Output { get; } + + public void Dispose() + { + this.stopwatch.Stop(); + this.Output.WriteLine($"{this.operation} completed in {this.stopwatch.ElapsedMilliseconds}ms"); + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index 645a4dc59..a8f7acb40 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; +namespace SixLabors.ImageSharp.Tests +{ /// /// Flags that are mapped to PackedPixel types. /// They trigger the desired parametrization for . /// [Flags] - public enum PixelTypes : uint + public enum PixelTypes { Undefined = 0, @@ -52,11 +50,11 @@ namespace ImageSharp.Tests Short4 = 1 << 17, - Rgb24 = 18, + Rgb24 = 1 << 18, - Bgr24 = 19, + Bgr24 = 1 << 19, - Bgra32 = 20, + Bgra32 = 1 << 20, // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs new file mode 100644 index 000000000..a201b39bd --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -0,0 +1,192 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Drawing.Imaging; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public static class SystemDrawingBridge + { + // TODO: It would be nice to have this method in PixelOperations + private static void ToArgb32(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgba32 s = ref rgbaBuffer[i]; + ref Argb32 d = ref dest[i]; + + d.PackFromRgba32(s); + } + } + } + + private static void FromArgb32(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgba32(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgba32 s = ref rgbaBuffer[i]; + ref TPixel d = ref dest[i]; + + d.PackFromRgba32(s); + } + } + } + + private static void FromRgb24(Span source, Span dest) + where TPixel : struct, IPixel + { + int length = source.Length; + Guard.MustBeSizedAtLeast(dest, length, nameof(dest)); + + using (var rgbaBuffer = new Buffer(length)) + { + PixelOperations.Instance.ToRgb24(source, rgbaBuffer, length); + + for (int i = 0; i < length; i++) + { + ref Rgb24 s = ref rgbaBuffer[i]; + ref TPixel d = ref dest[i]; + var rgba = default(Rgba32); + s.ToRgba32(ref rgba); + + d.PackFromRgba32(rgba); + } + } + } + + internal static unsafe Image FromFromArgb32SystemDrawingBitmap(System.Drawing.Bitmap bmp) + where TPixel : struct, IPixel + { + int w = bmp.Width; + int h = bmp.Height; + + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + + if (bmp.PixelFormat != PixelFormat.Format32bppArgb) + { + throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Argb32!", nameof(bmp)); + } + + BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); + byte* sourcePtrBase = (byte*)data.Scan0; + + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Argb32); + + var image = new Image(w, h); + + using (var workBuffer = new Buffer(w)) + { + var destPtr = (Argb32*)workBuffer.Pin(); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + + byte* sourcePtr = sourcePtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + + FromArgb32(workBuffer, row); + } + } + + return image; + } + + /// + /// TODO: Doesn not work yet! + /// + internal static unsafe Image FromFromRgb24SystemDrawingBitmap(System.Drawing.Bitmap bmp) + where TPixel : struct, IPixel + { + int w = bmp.Width; + int h = bmp.Height; + + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + + if (bmp.PixelFormat != PixelFormat.Format24bppRgb) + { + throw new ArgumentException($"FromFromArgb32SystemDrawingBitmap(): pixel format should be Rgb24!", nameof(bmp)); + } + + BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); + byte* sourcePtrBase = (byte*)data.Scan0; + + long sourceRowByteCount = data.Stride; + long destRowByteCount = w * sizeof(Rgb24); + + var image = new Image(w, h); + + using (var workBuffer = new Buffer(w)) + { + var destPtr = (Rgb24*)workBuffer.Pin(); + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + + byte* sourcePtr = sourcePtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + + FromRgb24(workBuffer, row); + } + } + + return image; + } + + internal static unsafe System.Drawing.Bitmap ToSystemDrawingBitmap(Image image) + where TPixel : struct, IPixel + { + int w = image.Width; + int h = image.Height; + + var resultBitmap = new System.Drawing.Bitmap(w, h, PixelFormat.Format32bppArgb); + var fullRect = new System.Drawing.Rectangle(0, 0, w, h); + BitmapData data = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat); + byte* destPtrBase = (byte*)data.Scan0; + + long destRowByteCount = data.Stride; + long sourceRowByteCount = w * sizeof(Argb32); + + using (var workBuffer = new Buffer(w)) + { + var sourcePtr = (Argb32*)workBuffer.Pin(); + + for (int y = 0; y < h; y++) + { + Span row = image.Frames.RootFrame.GetPixelRowSpan(y); + ToArgb32(row, workBuffer); + byte* destPtr = destPtrBase + data.Stride * y; + + Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); + } + } + + resultBitmap.UnlockBits(data); + + return resultBitmap; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs new file mode 100644 index 000000000..23ff61eb3 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using PixelFormat = System.Drawing.Imaging.PixelFormat; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public class SystemDrawingReferenceDecoder : IImageDecoder + { + public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); + + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + using (var sourceBitmap = new System.Drawing.Bitmap(stream)) + { + if (sourceBitmap.PixelFormat == PixelFormat.Format32bppArgb) + { + return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sourceBitmap); + } + + using (var convertedBitmap = new System.Drawing.Bitmap( + sourceBitmap.Width, + sourceBitmap.Height, + System.Drawing.Imaging.PixelFormat.Format32bppArgb)) + { + using (var g = Graphics.FromImage(convertedBitmap)) + { + g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + + g.DrawImage(sourceBitmap, 0, 0, sourceBitmap.Width, sourceBitmap.Height); + } + return SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(convertedBitmap); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs new file mode 100644 index 000000000..e1ef68fa6 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.Text; + +using System.Drawing.Imaging; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public class SystemDrawingReferenceEncoder : IImageEncoder + { + private readonly System.Drawing.Imaging.ImageFormat imageFormat; + + public SystemDrawingReferenceEncoder(ImageFormat imageFormat) + { + this.imageFormat = imageFormat; + } + + public static SystemDrawingReferenceEncoder Png { get; } = new SystemDrawingReferenceEncoder(System.Drawing.Imaging.ImageFormat.Png); + + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel + { + using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.ToSystemDrawingBitmap(image)) + { + sdBitmap.Save(stream, this.imageFormat); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs new file mode 100644 index 000000000..9eb051e7a --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs @@ -0,0 +1,32 @@ +using System; + +namespace SixLabors.ImageSharp.Tests +{ + internal static class TestDataGenerator + { + public static float[] GenerateRandomFloatArray(this Random rnd, int length, float minVal, float maxVal) + { + float[] values = new float[length]; + + for (int i = 0; i < length; i++) + { + values[i] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal; + } + + return values; + } + + public static float[] GenerateRandomRoundedFloatArray(this Random rnd, int length, int minVal, int maxValExclusive) + { + float[] values = new float[length]; + + for (int i = 0; i < length; i++) + { + int val = rnd.Next(minVal, maxValExclusive); + values[i] = (float)val; + } + + return values; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs new file mode 100644 index 000000000..089fed6b0 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -0,0 +1,84 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +namespace SixLabors.ImageSharp.Tests +{ + public static partial class TestEnvironment + { + private static Lazy configuration = new Lazy(CreateDefaultConfiguration); + + internal static Configuration Configuration => configuration.Value; + + internal static IImageDecoder GetReferenceDecoder(string filePath) + { + IImageFormat format = GetImageFormat(filePath); + return Configuration.FindDecoder(format); + } + + internal static IImageEncoder GetReferenceEncoder(string filePath) + { + IImageFormat format = GetImageFormat(filePath); + return Configuration.FindEncoder(format); + } + + internal static IImageFormat GetImageFormat(string filePath) + { + string extension = Path.GetExtension(filePath).ToLower(); + if (extension[0] == '.') extension = extension.Substring(1); + IImageFormat format = Configuration.FindFormatByFileExtension(extension); + return format; + } + + private static void ConfigureCodecs( + this Configuration cfg, + IImageFormat imageFormat, + IImageDecoder decoder, + IImageEncoder encoder, + IImageFormatDetector detector) + { + cfg.SetDecoder(imageFormat, decoder); + cfg.SetEncoder(imageFormat, encoder); + cfg.AddImageFormatDetector(detector); + } + + private static Configuration CreateDefaultConfiguration() + { + var configuration = new Configuration( + new PngConfigurationModule(), + new JpegConfigurationModule(), + new GifConfigurationModule() + ); + + if (!IsLinux) + { + configuration.ConfigureCodecs( + ImageFormats.Png, + SystemDrawingReferenceDecoder.Instance, + SystemDrawingReferenceEncoder.Png, + new PngImageFormatDetector()); + + configuration.ConfigureCodecs( + ImageFormats.Bmp, + SystemDrawingReferenceDecoder.Instance, + SystemDrawingReferenceEncoder.Png, + new PngImageFormatDetector()); + } + else + { + configuration.Configure(new PngConfigurationModule()); + configuration.Configure(new BmpConfigurationModule()); + } + + return configuration; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs new file mode 100644 index 000000000..d2282f399 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -0,0 +1,123 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Tests +{ + public static partial class TestEnvironment + { + private const string ImageSharpSolutionFileName = "ImageSharp.sln"; + + private const string InputImagesRelativePath = @"tests\Images\Input"; + + private const string ActualOutputDirectoryRelativePath = @"tests\Images\ActualOutput"; + + private const string ReferenceOutputDirectoryRelativePath = @"tests\Images\External\ReferenceOutput"; + + private const string ToolsDirectoryRelativePath = @"tests\Images\External\tools"; + + private static Lazy solutionDirectoryFullPath = new Lazy(GetSolutionDirectoryFullPathImpl); + + private static Lazy runsOnCi = new Lazy( + () => + { + bool isCi; + return Boolean.TryParse(Environment.GetEnvironmentVariable("CI"), out isCi) && isCi; + }); + + // ReSharper disable once InconsistentNaming + /// + /// Gets a value indicating whether test execution runs on CI. + /// + internal static bool RunsOnCI => runsOnCi.Value; + + internal static string SolutionDirectoryFullPath => solutionDirectoryFullPath.Value; + + private static string GetSolutionDirectoryFullPathImpl() + { + string assemblyLocation = typeof(TestEnvironment).GetTypeInfo().Assembly.Location; + + var assemblyFile = new FileInfo(assemblyLocation); + + DirectoryInfo directory = assemblyFile.Directory; + + while (!directory.EnumerateFiles(ImageSharpSolutionFileName).Any()) + { + try + { + directory = directory.Parent; + } + catch (Exception ex) + { + throw new Exception( + $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", + ex); + } + if (directory == null) + { + throw new Exception($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); + } + } + + return directory.FullName; + } + + private static string GetFullPath(string relativePath) => + Path.Combine(SolutionDirectoryFullPath, relativePath) + .Replace('\\', Path.DirectorySeparatorChar); + + /// + /// Gets the correct full path to the Input Images directory. + /// + internal static string InputImagesDirectoryFullPath => GetFullPath(InputImagesRelativePath); + + /// + /// Gets the correct full path to the Actual Output directory. (To be written to by the test cases.) + /// + internal static string ActualOutputDirectoryFullPath => GetFullPath(ActualOutputDirectoryRelativePath); + + /// + /// Gets the correct full path to the Expected Output directory. (To compare the test results to.) + /// + internal static string ReferenceOutputDirectoryFullPath => GetFullPath(ReferenceOutputDirectoryRelativePath); + + internal static string ToolsDirectoryFullPath => GetFullPath(ToolsDirectoryRelativePath); + + internal static string GetReferenceOutputFileName(string actualOutputFileName) => + actualOutputFileName.Replace("ActualOutput", @"External\ReferenceOutput").Replace('\\', Path.DirectorySeparatorChar); + + internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + internal static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + /// + /// Creates the image output directory. + /// + /// The path. + /// The path parts. + /// + /// The . + /// + internal static string CreateOutputDirectory(string path, params string[] pathParts) + { + path = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, path); + + if (pathParts != null && pathParts.Length > 0) + { + path = Path.Combine(path, Path.Combine(pathParts)); + } + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + return path; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index bb97daaa4..b3dd763c7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -1,16 +1,21 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; - using ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ + using System.Numerics; + using SixLabors.ImageSharp.Advanced; + using SixLabors.ImageSharp.Memory; public static class TestImageExtensions { @@ -20,42 +25,187 @@ namespace ImageSharp.Tests /// The pixel format /// The image /// The image provider - /// The settings + /// Details to be concatenated to the test output file, describing the parameters of the test. /// The extension - public static Image DebugSave(this Image image, ITestImageProvider provider, object settings = null, string extension = "png") + /// A boolean indicating whether we should save a smaller in size. + /// A boolean indicating whether to append the pixel type to the output file name. + public static Image DebugSave( + this Image image, + ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) where TPixel : struct, IPixel { - if (bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi) + if (TestEnvironment.RunsOnCI) { return image; } // We are running locally then we want to save it out - string tag = null; - string s = settings as string; + provider.Utility.SaveTestOutputFile( + image, + extension, + testOutputDetails: testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName); + return image; + } + + /// + /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. + /// The output file should be named identically to the output produced by . + /// + /// The pixel format + /// The image + /// The image provider + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension + /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. + /// A boolean indicating whether to append the pixel type to the output file name. + /// + public static Image CompareToReferenceOutput( + this Image image, + ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + return CompareToReferenceOutput( + image, + provider, + ImageComparer.Tolerant(), + testOutputDetails, + extension, + grayscale, + appendPixelTypeToFileName); + } + + /// + /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. + /// The output file should be named identically to the output produced by . + /// + /// The pixel format + /// The image + /// The image provider + /// The to use + /// Details to be concatenated to the test output file, describing the parameters of the test. + /// The extension + /// A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size. + /// A boolean indicating whether to append the pixel type to the output file name. + /// + public static Image CompareToReferenceOutput( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image referenceImage = GetReferenceOutputImage( + provider, + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + comparer.VerifySimilarity(referenceImage, image); + } + + return image; + } + + public static Image GetReferenceOutputImage(this ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); - if (s != null) + if (!File.Exists(referenceOutputFile)) { - tag = s; + throw new Exception("Reference output file missing: " + referenceOutputFile); } - else if (settings != null) + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); + + return Image.Load(referenceOutputFile, decoder); + } + + public static IEnumerable GetReferenceOutputSimilarityReports( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image referenceImage = provider.GetReferenceOutputImage( + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + return comparer.CompareImages(referenceImage, image); + } + } + + public static Image CompareToOriginal( + this Image image, + ITestImageProvider provider) + where TPixel : struct, IPixel + { + return CompareToOriginal(image, provider, ImageComparer.Tolerant()); + } + + public static Image CompareToOriginal( + this Image image, + ITestImageProvider provider, + ImageComparer comparer) + where TPixel : struct, IPixel + { + string path = TestImageProvider.GetFilePathOrNull(provider); + if (path == null) + { + throw new InvalidOperationException("CompareToOriginal() works only with file providers!"); + } + + var testFile = TestFile.Create(path); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + IImageFormat format = TestEnvironment.GetImageFormat(path); + IImageDecoder defaultDecoder = Configuration.Default.FindDecoder(format); + + //if (referenceDecoder.GetType() == defaultDecoder.GetType()) + //{ + // throw new InvalidOperationException($"Can't use CompareToOriginal(): no actual reference decoder registered for {format.Name}"); + //} + + using (var original = Image.Load(testFile.Bytes, referenceDecoder)) + { + comparer.VerifySimilarity(original, image); + } + + return image; + } + + internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) + { + var image = new Image(buffer.Width, buffer.Height); + + Span pixels = image.Frames.RootFrame.GetPixelSpan(); + + for (int i = 0; i < buffer.Length; i++) { - Type type = settings.GetType(); - TypeInfo info = type.GetTypeInfo(); - if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) - { - tag = settings.ToString(); - } - else - { - IEnumerable properties = settings.GetType().GetRuntimeProperties(); - - tag = string.Join("_", properties.ToDictionary(x => x.Name, x => x.GetValue(settings)).Select(x => $"{x.Key}-{x.Value}")); - } + float value = buffer[i] * scale; + var v = new Vector4(value, value, value, 1f); + pixels[i].PackFromVector4(v); } - provider.Utility.SaveTestOutputFile(image, extension, tag: tag); return image; } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index 852bc587d..1eb0aafff 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -1,10 +1,13 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Collections.Generic; using System.Text; -using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; -namespace ImageSharp.Tests.TestUtilities +namespace SixLabors.ImageSharp.Tests.TestUtilities { public class TestPixel : IXunitSerializable where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Tests/TestUtilities/TestType.cs b/tests/ImageSharp.Tests/TestUtilities/TestType.cs new file mode 100644 index 000000000..788a0543a --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestType.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Text; +using SixLabors.ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.TestUtilities +{ + public class TestType : IXunitSerializable + { + public TestType() + { + } + + public void Deserialize(IXunitSerializationInfo info) + { + } + + public void Serialize(IXunitSerializationInfo info) + { + } + + public override string ToString() + { + return $"Type<{typeof(T).Name}>"; + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs similarity index 83% rename from tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs rename to tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index e42cbe193..83997841a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -1,22 +1,19 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using System.Reflection; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Tests +{ /// - /// Extension methods for TestUtilities + /// Various utility and extension methods. /// - public static class TestUtilityExtensions + public static class TestUtils { private static readonly Dictionary ClrTypes2PixelTypes = new Dictionary(); @@ -28,7 +25,7 @@ namespace ImageSharp.Tests .Except(new[] { PixelTypes.Undefined, PixelTypes.All }) .ToArray(); - static TestUtilityExtensions() + static TestUtils() { // Add Rgba32 Our default. Type defaultPixelFormatType = typeof(Rgba32); @@ -103,7 +100,7 @@ namespace ImageSharp.Tests return string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o))); } - public static Type ToType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; + public static Type GetClrType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; /// /// Returns the enumerations for the given type. @@ -112,6 +109,8 @@ namespace ImageSharp.Tests /// public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; + + public static IEnumerable> ExpandAllTypes(this PixelTypes pixelTypes) { if (pixelTypes == PixelTypes.Undefined) @@ -124,11 +123,20 @@ namespace ImageSharp.Tests return PixelTypes2ClrTypes; } - return AllConcretePixelTypes - .Where(pt => pixelTypes.HasFlag(pt)) - .Select(pt => new KeyValuePair(pt, pt.ToType())); + var result = new Dictionary(); + foreach (PixelTypes pt in AllConcretePixelTypes) + { + if (pixelTypes.HasAll(pt)) + { + result[pt] = pt.GetClrType(); + } + } + return result; } + internal static bool HasAll(this PixelTypes pixelTypes, PixelTypes flagsToCheck) => + (pixelTypes & flagsToCheck) == flagsToCheck; + /// /// Enumerate all available -s /// diff --git a/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs index beb3fcd97..3916f189c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs @@ -1,11 +1,14 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Collections.Generic; using System.Numerics; using System.Text; -using ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats; using Xunit.Abstractions; -namespace ImageSharp.Tests.TestUtilities +namespace SixLabors.ImageSharp.Tests.TestUtilities { public class TestVector4 : IXunitSerializable { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs new file mode 100644 index 000000000..f131f51f2 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs @@ -0,0 +1,184 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using System.Collections.Generic; + using System.Linq; + + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Moq; + + using SixLabors.Primitives; + + using Xunit; + using Xunit.Abstractions; + + public class ImageComparerTests + { + public ImageComparerTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + [Theory] + [WithTestPatternImages(100,100,PixelTypes.Rgba32, 0.0001f, 1)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0, 0)] + public void TolerantImageComparer_ApprovesPerfectSimilarity( + TestImageProvider provider, + float imageTheshold, + int pixelThreshold) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + var comparer = ImageComparer.Tolerant(imageTheshold, pixelThreshold); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(110, 110, PixelTypes.Rgba32)] + public void TolerantImageComparer_ApprovesSimilarityBelowTolerance(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 1); + + var comparer = ImageComparer.Tolerant(); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TolerantImageComparer_DoesNotApproveSimilarityAboveTolerance(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 3, 1, 2); + + var comparer = ImageComparer.Tolerant(); + + ImageDifferenceIsOverThresholdException ex = Assert.ThrowsAny( + () => + { + comparer.VerifySimilarity(image, clone); + }); + PixelDifference diff = ex.Reports.Single().Differences.Single(); + Assert.Equal(new Point(3, 1), diff.Position); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void TolerantImageComparer_TestPerPixelThreshold(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 0, 0, 10); + ImagingTestCaseUtility.ModifyPixel(clone, 1, 0, 10); + ImagingTestCaseUtility.ModifyPixel(clone, 2, 0, 10); + + var comparer = ImageComparer.Tolerant(perPixelManhattanThreshold: 42); + comparer.VerifySimilarity(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 99, 100)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 100, 99)] + public void VerifySimilarity_ThrowsOnSizeMismatch(TestImageProvider provider, int w, int h) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone(ctx => ctx.Resize(w, h))) + { + ImageDimensionsMismatchException ex = Assert.ThrowsAny( + () => + { + ImageComparer comparer = Mock.Of(); + comparer.VerifySimilarity(image, clone); + }); + this.Output.WriteLine(ex.Message); + } + } + } + + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void VerifySimilarity_WhenAnImageFrameIsDifferent_Reports(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone.Frames[0], 42, 43, 1); + + IEnumerable reports = ImageComparer.Exact.CompareImages(image, clone); + + PixelDifference difference = reports.Single().Differences.Single(); + Assert.Equal(new Point(42, 43), difference.Position); + } + } + } + + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void ExactComparer_ApprovesExactEquality(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ExactImageComparer.Instance.CompareImages(image, clone); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void ExactComparer_DoesNotTolerateAnyPixelDifference(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + ImagingTestCaseUtility.ModifyPixel(clone, 42, 24, 1); + ImagingTestCaseUtility.ModifyPixel(clone, 7, 93, 1); + + IEnumerable reports = ExactImageComparer.Instance.CompareImages(image, clone); + + this.Output.WriteLine(reports.Single().ToString()); + PixelDifference[] differences = reports.Single().Differences; + Assert.Equal(2, differences.Length); + Assert.Contains(differences, d => d.Position == new Point(42, 24)); + Assert.Contains(differences, d => d.Position == new Point(7, 93)); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs new file mode 100644 index 000000000..0a550a3c1 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs @@ -0,0 +1,132 @@ +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats; + using SixLabors.ImageSharp.Formats.Png; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + + using Xunit; + using Xunit.Abstractions; + + public class ReferenceCodecTests + { + private ITestOutputHelper Output { get; } + + public ReferenceCodecTests(ITestOutputHelper output) + { + this.Output = output; + } + + [Theory] + [WithTestPatternImages(20, 20, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void ToSystemDrawingBitmap(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (System.Drawing.Bitmap sdBitmap = SystemDrawingBridge.ToSystemDrawingBitmap(image)) + { + string fileName = provider.Utility.GetTestOutputFileName("png"); + sdBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); + } + } + } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void FromFromArgb32SystemDrawingBitmap(TestImageProvider dummyProvider) + where TPixel : struct, IPixel + { + string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); + + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image image = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sdBitmap)) + { + image.DebugSave(dummyProvider); + } + } + } + + private static string SavePng(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + using (Image sourceImage = provider.GetImage()) + { + if (pngColorType != PngColorType.RgbWithAlpha) + { + sourceImage.Mutate(c => c.Alpha(1)); + } + + var encoder = new PngEncoder() { PngColorType = pngColorType }; + return provider.Utility.SaveTestOutputFile(sourceImage, "png", encoder); + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FromFromArgb32SystemDrawingBitmap2(TestImageProvider provider) + where TPixel : struct, IPixel + { + if (TestEnvironment.IsLinux) return; + + string path = SavePng(provider, PngColorType.RgbWithAlpha); + + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image original = provider.GetImage()) + using (Image resaved = SystemDrawingBridge.FromFromArgb32SystemDrawingBitmap(sdBitmap)) + { + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); + } + } + } + + [Theory(Skip = "Doesen't work yet :(")] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void FromFromRgb24SystemDrawingBitmap2(TestImageProvider provider) + where TPixel : struct, IPixel + { + string path = SavePng(provider, PngColorType.Rgb); + + using (Image original = provider.GetImage()) + { + original.Mutate(c => c.Alpha(1)); + using (var sdBitmap = new System.Drawing.Bitmap(path)) + { + using (Image resaved = SystemDrawingBridge.FromFromRgb24SystemDrawingBitmap(sdBitmap)) + { + resaved.Mutate(c => c.Alpha(1)); + ImageComparer comparer = ImageComparer.Exact; + comparer.VerifySimilarity(original, resaved); + } + } + } + } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void OpenWithReferenceDecoder(TestImageProvider dummyProvider) + where TPixel : struct, IPixel + { + string path = TestFile.GetInputFileFullPath(TestImages.Png.Splash); + using (Image image = Image.Load(path, SystemDrawingReferenceDecoder.Instance)) + { + image.DebugSave(dummyProvider); + } + } + + [Theory] + [WithTestPatternImages(20, 20, PixelTypes.Rgba32 | PixelTypes.Argb32)] + public void SaveWithReferenceEncoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "png", SystemDrawingReferenceEncoder.Png); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs new file mode 100644 index 000000000..658fa862d --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -0,0 +1,121 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; +using Xunit; +using Xunit.Abstractions; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats.Bmp; + using SixLabors.ImageSharp.Formats.Png; + + public class TestEnvironmentTests + { + public TestEnvironmentTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + private void CheckPath(string path) + { + this.Output.WriteLine(path); + Assert.True(Directory.Exists(path)); + } + + [Fact] + public void SolutionDirectoryFullPath() + { + this.CheckPath(TestEnvironment.SolutionDirectoryFullPath); + } + + [Fact] + public void InputImagesDirectoryFullPath() + { + this.CheckPath(TestEnvironment.InputImagesDirectoryFullPath); + } + + [Fact] + public void ActualOutputDirectoryFullPath() + { + this.CheckPath(TestEnvironment.ActualOutputDirectoryFullPath); + } + + [Fact] + public void ExpectedOutputDirectoryFullPath() + { + this.CheckPath(TestEnvironment.ReferenceOutputDirectoryFullPath); + } + + [Fact] + public void GetReferenceOutputFileName() + { + string actual = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, @"foo\bar\lol.jpeg"); + string expected = TestEnvironment.GetReferenceOutputFileName(actual); + + this.Output.WriteLine(expected); + Assert.Contains(TestEnvironment.ReferenceOutputDirectoryFullPath, expected); + } + + [Theory] + [InlineData("lol/foo.png", typeof(SystemDrawingReferenceEncoder))] + [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceEncoder))] + [InlineData("lol/Baz.JPG", typeof(JpegEncoder))] + [InlineData("lol/Baz.gif", typeof(GifEncoder))] + public void GetReferenceEncoder_ReturnsCorrectEncoders_Windows(string fileName, Type expectedEncoderType) + { + if (TestEnvironment.IsLinux) return; + + IImageEncoder encoder = TestEnvironment.GetReferenceEncoder(fileName); + Assert.IsType(expectedEncoderType, encoder); + } + + [Theory] + [InlineData("lol/foo.png", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] + [InlineData("lol/Baz.gif", typeof(GifDecoder))] + public void GetReferenceDecoder_ReturnsCorrectEncoders_Windows(string fileName, Type expectedDecoderType) + { + if (TestEnvironment.IsLinux) return; + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); + Assert.IsType(expectedDecoderType, decoder); + } + + [Theory] + [InlineData("lol/foo.png", typeof(PngEncoder))] + [InlineData("lol/Rofl.bmp", typeof(BmpEncoder))] + [InlineData("lol/Baz.JPG", typeof(JpegEncoder))] + [InlineData("lol/Baz.gif", typeof(GifEncoder))] + public void GetReferenceEncoder_ReturnsCorrectEncoders_Linux(string fileName, Type expectedEncoderType) + { + if (!TestEnvironment.IsLinux) return; + + IImageEncoder encoder = TestEnvironment.GetReferenceEncoder(fileName); + Assert.IsType(expectedEncoderType, encoder); + } + + [Theory] + [InlineData("lol/foo.png", typeof(PngDecoder))] + [InlineData("lol/Rofl.bmp", typeof(BmpDecoder))] + [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] + [InlineData("lol/Baz.gif", typeof(GifDecoder))] + public void GetReferenceDecoder_ReturnsCorrectEncoders_Linux(string fileName, Type expectedDecoderType) + { + if (!TestEnvironment.IsLinux) return; + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(fileName); + Assert.IsType(expectedDecoderType, decoder); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageExtensionsTests.cs new file mode 100644 index 000000000..45ac2d6cc --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageExtensionsTests.cs @@ -0,0 +1,109 @@ +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests +{ + using System; + + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Moq; + + using Xunit; + + public class TestImageExtensionsTests + { + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_WhenReferenceOutputMatches_ShouldNotThrow( + TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.CompareToReferenceOutput(provider); + } + } + + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_WhenReferenceOutputDoesNotMatch_Throws( + TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.ThrowsAny(() => image.CompareToReferenceOutput(provider)); + } + } + + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_DoNotAppendPixelType( + TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DebugSave(provider, appendPixelTypeToFileName: false); + image.CompareToReferenceOutput(provider, appendPixelTypeToFileName: false); + } + } + + [Theory] + [WithSolidFilledImages(10, 10, 0, 0, 255, PixelTypes.Rgba32)] + public void CompareToReferenceOutput_WhenReferenceFileMissing_Throws(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.ThrowsAny(() => image.CompareToReferenceOutput(provider)); + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void CompareToOriginal_WhenSimilar(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + using (Image clone = image.Clone()) + { + clone.CompareToOriginal(provider, ImageComparer.Exact); + } + } + } + + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] + public void CompareToOriginal_WhenDifferent_Throws(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + ImagingTestCaseUtility.ModifyPixel(image, 3, 1, 1); + + Assert.ThrowsAny( + () => + { + image.CompareToOriginal(provider, ImageComparer.Exact); + }); + } + } + + [Theory] + [WithBlankImages(10, 10, PixelTypes.Rgba32)] + public void CompareToOriginal_WhenInputIsNotFromFile_Throws(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + Assert.ThrowsAny( + () => + { + image.CompareToOriginal(provider, Mock.Of()); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index c01babb63..88d7fa2b8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -1,17 +1,18 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; +using System; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; - using ImageSharp.PixelFormats; +using System.Collections.Concurrent; +using System.IO; - using Xunit; - using Xunit.Abstractions; +using SixLabors.ImageSharp.Formats; +namespace SixLabors.ImageSharp.Tests +{ public class TestImageProviderTests { public TestImageProviderTests(ITestOutputHelper output) @@ -50,25 +51,13 @@ namespace ImageSharp.Tests [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] - [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] + [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) where TPixel : struct, IPixel { Assert.Equal(expected, provider.PixelType); } - - [Theory] - [WithBlankImages(1, 1, PixelTypes.Rgba32)] - [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] - public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass( - TestImageProvider provider) - where TPixel : struct, IPixel - { - Image img = provider.GetImage(); - - Assert.IsType>(img); - } - + [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] @@ -85,6 +74,155 @@ namespace ImageSharp.Tests this.Output.WriteLine(fn); } + private class TestDecoder : IImageDecoder + { + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + // Couldn't make xUnit happy without this hackery: + + private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + + private string callerName = null; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + private static readonly object Monitor = new object(); + + public static void DoTestThreadSafe(Action action) + { + lock (Monitor) + { + action(); + } + } + } + + + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); + + TestDecoder.DoTestThreadSafe( + () => + { + string testName = nameof(this.GetImage_WithCustomParameterlessDecoder_ShouldUtilizeCache); + + var decoder = new TestDecoder(); + decoder.InitCaller(testName); + + provider.GetImage(decoder); + Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); + + provider.GetImage(decoder); + Assert.Equal(1, TestDecoder.GetInvocationCount(testName)); + }); + } + + private class TestDecoderWithParameters : IImageDecoder + { + public string Param1 { get; set; } + + public int Param2 { get; set; } + + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + invocationCounts[this.callerName]++; + return new Image(42, 42); + } + + private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + + private string callerName = null; + + internal void InitCaller(string name) + { + this.callerName = name; + invocationCounts[name] = 0; + } + + internal static int GetInvocationCount(string callerName) => invocationCounts[callerName]; + + private static readonly object Monitor = new object(); + + public static void DoTestThreadSafe(Action action) + { + lock (Monitor) + { + action(); + } + } + } + + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); + + TestDecoderWithParameters.DoTestThreadSafe( + () => + { + string testName = + nameof(this.GetImage_WithCustomParametricDecoder_ShouldUtilizeCache_WhenParametersAreEqual); + + var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; + decoder1.InitCaller(testName); + + var decoder2 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 666 }; + decoder2.InitCaller(testName); + + provider.GetImage(decoder1); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + + provider.GetImage(decoder2); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + }); + } + + [Theory] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] + public void GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.NotNull(provider.Utility.SourceFileOrDescription); + + TestDecoderWithParameters.DoTestThreadSafe( + () => + { + string testName = + nameof(this.GetImage_WithCustomParametricDecoder_ShouldNotUtilizeCache_WhenParametersAreNotEqual); + + var decoder1 = new TestDecoderWithParameters() { Param1 = "Lol", Param2 = 42 }; + decoder1.InitCaller(testName); + + var decoder2 = new TestDecoderWithParameters() { Param1 = "LoL", Param2 = 42 }; + decoder2.InitCaller(testName); + + provider.GetImage(decoder1); + Assert.Equal(1, TestDecoderWithParameters.GetInvocationCount(testName)); + + provider.GetImage(decoder2); + Assert.Equal(2, TestDecoderWithParameters.GetInvocationCount(testName)); + }); + } + + public static string[] AllBmpFiles => TestImages.Bmp.All; [Theory] @@ -96,7 +234,7 @@ namespace ImageSharp.Tests Image image = provider.GetImage(); provider.Utility.SaveTestOutputFile(image, "png"); } - + [Theory] [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Rgba32 | PixelTypes.Argb32)] public void Use_WithSolidFilledImagesAttribute(TestImageProvider provider) @@ -131,10 +269,10 @@ namespace ImageSharp.Tests /// /// /// - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - return factory.CreateImage(3, 3); + return new Image(3, 3); } [Theory] @@ -152,12 +290,12 @@ namespace ImageSharp.Tests } public static readonly TheoryData BasicData = new TheoryData() - { - TestImageProvider.Blank(10, 20), - TestImageProvider.Blank( - 10, - 20), - }; + { + TestImageProvider.Blank(10, 20), + TestImageProvider.Blank( + 10, + 20), + }; [Theory] [MemberData(nameof(BasicData))] @@ -170,12 +308,10 @@ namespace ImageSharp.Tests } public static readonly TheoryData FileData = new TheoryData() - { - TestImageProvider.File( - TestImages.Bmp.Car), - TestImageProvider.File( - TestImages.Bmp.F) - }; + { + TestImageProvider.File(TestImages.Bmp.Car), + TestImageProvider.File(TestImages.Bmp.F) + }; [Theory] [MemberData(nameof(FileData))] @@ -190,4 +326,4 @@ namespace ImageSharp.Tests Assert.True(img.Width * img.Height > 0); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 437c295b9..fe5af24d2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -1,21 +1,17 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Reflection; - - using ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; - using Xunit; - using Xunit.Abstractions; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; +namespace SixLabors.ImageSharp.Tests +{ public class TestUtilityExtensionsTests { public TestUtilityExtensionsTests(ITestOutputHelper output) @@ -25,10 +21,10 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } - public static Image CreateTestImage(GenericFactory factory) + public static Image CreateTestImage() where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + var image = new Image(10, 10); using (PixelAccessor pixels = image.Lock()) { @@ -36,10 +32,10 @@ namespace ImageSharp.Tests { for (int j = 0; j < 10; j++) { - Vector4 v = new Vector4(i, j, 0, 1); + var v = new Vector4(i, j, 0, 1); v /= 10; - TPixel color = default(TPixel); + var color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -50,16 +46,6 @@ namespace ImageSharp.Tests return image; } - [Fact] - public void Baz() - { - Type type = typeof(Rgba32).GetTypeInfo().Assembly.GetType("ImageSharp.Rgba32"); - this.Output.WriteLine(type.ToString()); - - Type fake = typeof(Rgba32).GetTypeInfo().Assembly.GetType("ImageSharp.dsaada_DASqewrr"); - Assert.Null(fake); - } - [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, true)] [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, false)] @@ -67,8 +53,7 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { Image a = provider.GetImage(); - Image b = provider.GetImage(); - b = b.OilPaint(3, 2); + Image b = provider.GetImage(x=>x.OilPaint(3, 2)); Assert.False(a.IsEquivalentTo(b, compareAlpha)); } @@ -89,10 +74,9 @@ namespace ImageSharp.Tests [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] [InlineData(PixelTypes.Argb32, typeof(Argb32))] [InlineData(PixelTypes.HalfVector4, typeof(HalfVector4))] - [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] public void ToType(PixelTypes pt, Type expectedType) { - Assert.Equal(pt.ToType(), expectedType); + Assert.Equal(pt.GetClrType(), expectedType); } [Theory] @@ -112,7 +96,7 @@ namespace ImageSharp.Tests } [Fact] - public void ToTypes() + public void ExpandAllTypes_1() { PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.HalfVector2 | PixelTypes.Rgba32; @@ -126,12 +110,26 @@ namespace ImageSharp.Tests AssertContainsPixelType(PixelTypes.Rgba32, expanded); } + [Fact] + public void ExpandAllTypes_2() + { + PixelTypes pixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + + IEnumerable> expanded = pixelTypes.ExpandAllTypes(); + + Assert.Equal(3, expanded.Count()); + + AssertContainsPixelType(PixelTypes.Rgba32, expanded); + AssertContainsPixelType(PixelTypes.Bgra32, expanded); + AssertContainsPixelType(PixelTypes.RgbaVector, expanded); + } + [Fact] public void ToTypes_All() { KeyValuePair[] expanded = PixelTypes.All.ExpandAllTypes().ToArray(); - Assert.True(expanded.Length >= TestUtilityExtensions.GetAllPixelTypes().Length - 2); + Assert.True(expanded.Length >= TestUtils.GetAllPixelTypes().Length - 2); AssertContainsPixelType(PixelTypes.Rgba32, expanded); AssertContainsPixelType(PixelTypes.Rgba32, expanded); } diff --git a/tests/ImageSharp.Tests/VectorAssert.cs b/tests/ImageSharp.Tests/VectorAssert.cs index ad26963d4..402d06655 100644 --- a/tests/ImageSharp.Tests/VectorAssert.cs +++ b/tests/ImageSharp.Tests/VectorAssert.cs @@ -1,18 +1,16 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// + +using System; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; // ReSharper disable MemberHidesStaticFromOuterClass -namespace ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests { - using System; - using System.Collections.Generic; - using System.Numerics; - using ImageSharp; - using ImageSharp.PixelFormats; - using Xunit; - /// /// Class to perform simple image comparisons. /// diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json index df1c3d50d..cbaa8f432 100644 --- a/tests/ImageSharp.Tests/xunit.runner.json +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -1,3 +1,4 @@ { - "methodDisplay": "method" + "methodDisplay": "method", + "diagnosticMessages": true } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External new file mode 160000 index 000000000..f99c2ea41 --- /dev/null +++ b/tests/Images/External @@ -0,0 +1 @@ +Subproject commit f99c2ea41419cb3b3e80e5beeab611682252df78 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/BITMAPV5HEADER.bmp b/tests/Images/Input/Bmp/BITMAPV5HEADER.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/BITMAPV5HEADER.bmp rename to tests/Images/Input/Bmp/BITMAPV5HEADER.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp b/tests/Images/Input/Bmp/BitmapCoreHeaderQR.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp rename to tests/Images/Input/Bmp/BitmapCoreHeaderQR.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp b/tests/Images/Input/Bmp/Car.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp rename to tests/Images/Input/Bmp/Car.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/F.bmp b/tests/Images/Input/Bmp/F.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/F.bmp rename to tests/Images/Input/Bmp/F.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/neg_height.bmp b/tests/Images/Input/Bmp/neg_height.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/neg_height.bmp rename to tests/Images/Input/Bmp/neg_height.bmp diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/cheers.gif b/tests/Images/Input/Gif/cheers.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/cheers.gif rename to tests/Images/Input/Gif/cheers.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/giphy.gif b/tests/Images/Input/Gif/giphy.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/giphy.gif rename to tests/Images/Input/Gif/giphy.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif b/tests/Images/Input/Gif/rings.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif rename to tests/Images/Input/Gif/rings.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif b/tests/Images/Input/Gif/trans.gif similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif rename to tests/Images/Input/Gif/trans.gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Calliphora.jpg b/tests/Images/Input/Jpg/baseline/Calliphora.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Calliphora.jpg rename to tests/Images/Input/Jpg/baseline/Calliphora.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg b/tests/Images/Input/Jpg/baseline/ExifUndefType.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg rename to tests/Images/Input/Jpg/baseline/ExifUndefType.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Floorplan.jpg b/tests/Images/Input/Jpg/baseline/Floorplan.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Floorplan.jpg rename to tests/Images/Input/Jpg/baseline/Floorplan.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Hiyamugi.jpg b/tests/Images/Input/Jpg/baseline/Hiyamugi.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Hiyamugi.jpg rename to tests/Images/Input/Jpg/baseline/Hiyamugi.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Lake.jpg b/tests/Images/Input/Jpg/baseline/Lake.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Lake.jpg rename to tests/Images/Input/Jpg/baseline/Lake.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Snake.jpg b/tests/Images/Input/Jpg/baseline/Snake.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/Snake.jpg rename to tests/Images/Input/Jpg/baseline/Snake.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/badeof.jpg b/tests/Images/Input/Jpg/baseline/badeof.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/badeof.jpg rename to tests/Images/Input/Jpg/baseline/badeof.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/cmyk.jpg b/tests/Images/Input/Jpg/baseline/cmyk.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/cmyk.jpg rename to tests/Images/Input/Jpg/baseline/cmyk.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/exif.jpg b/tests/Images/Input/Jpg/baseline/exif.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/exif.jpg rename to tests/Images/Input/Jpg/baseline/exif.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/gamma_dalai_lama_gray.jpg b/tests/Images/Input/Jpg/baseline/gamma_dalai_lama_gray.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/gamma_dalai_lama_gray.jpg rename to tests/Images/Input/Jpg/baseline/gamma_dalai_lama_gray.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg400jfif.jpg b/tests/Images/Input/Jpg/baseline/jpeg400jfif.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg400jfif.jpg rename to tests/Images/Input/Jpg/baseline/jpeg400jfif.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg b/tests/Images/Input/Jpg/baseline/jpeg420exif.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg rename to tests/Images/Input/Jpg/baseline/jpeg420exif.jpg diff --git a/tests/Images/Input/Jpg/baseline/jpeg420small.jpg b/tests/Images/Input/Jpg/baseline/jpeg420small.jpg new file mode 100644 index 000000000..f4c3f4736 Binary files /dev/null and b/tests/Images/Input/Jpg/baseline/jpeg420small.jpg differ diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg444.jpg b/tests/Images/Input/Jpg/baseline/jpeg444.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg444.jpg rename to tests/Images/Input/Jpg/baseline/jpeg444.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testimgint.jpg b/tests/Images/Input/Jpg/baseline/testimgint.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testimgint.jpg rename to tests/Images/Input/Jpg/baseline/testimgint.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testorig.jpg b/tests/Images/Input/Jpg/baseline/testorig.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/testorig.jpg rename to tests/Images/Input/Jpg/baseline/testorig.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/turtle.jpg b/tests/Images/Input/Jpg/baseline/turtle.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/turtle.jpg rename to tests/Images/Input/Jpg/baseline/turtle.jpg diff --git a/tests/Images/Input/Jpg/baseline/ycck.jpg b/tests/Images/Input/Jpg/baseline/ycck.jpg new file mode 100644 index 000000000..30e88773e Binary files /dev/null and b/tests/Images/Input/Jpg/baseline/ycck.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg b/tests/Images/Input/Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg new file mode 100644 index 000000000..f762e7a69 Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg b/tests/Images/Input/Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg new file mode 100644 index 000000000..91bc260d8 Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg b/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg new file mode 100644 index 000000000..84b22c9da Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue214-CriticalEOF .jpg differ diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/BadEofProgressive.jpg b/tests/Images/Input/Jpg/progressive/BadEofProgressive.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/BadEofProgressive.jpg rename to tests/Images/Input/Jpg/progressive/BadEofProgressive.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/Festzug.jpg b/tests/Images/Input/Jpg/progressive/Festzug.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/Festzug.jpg rename to tests/Images/Input/Jpg/progressive/Festzug.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/fb.jpg b/tests/Images/Input/Jpg/progressive/fb.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/fb.jpg rename to tests/Images/Input/Jpg/progressive/fb.jpg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/progress.jpg b/tests/Images/Input/Jpg/progressive/progress.jpg similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Jpg/progressive/progress.jpg rename to tests/Images/Input/Jpg/progressive/progress.jpg diff --git a/tests/Images/Input/Png/Bike.png b/tests/Images/Input/Png/Bike.png new file mode 100644 index 000000000..71f196923 Binary files /dev/null and b/tests/Images/Input/Png/Bike.png differ diff --git a/tests/Images/Input/Png/BikeGrayscale.png b/tests/Images/Input/Png/BikeGrayscale.png new file mode 100644 index 000000000..4af618d88 Binary files /dev/null and b/tests/Images/Input/Png/BikeGrayscale.png differ diff --git a/tests/Images/Input/Png/CalliphoraPartial.png b/tests/Images/Input/Png/CalliphoraPartial.png new file mode 100644 index 000000000..e05ad0469 Binary files /dev/null and b/tests/Images/Input/Png/CalliphoraPartial.png differ diff --git a/tests/Images/Input/Png/CalliphoraPartialGrayscale.png b/tests/Images/Input/Png/CalliphoraPartialGrayscale.png new file mode 100644 index 000000000..237148dcc Binary files /dev/null and b/tests/Images/Input/Png/CalliphoraPartialGrayscale.png differ diff --git a/tests/Images/Input/Png/SnakeGame.png b/tests/Images/Input/Png/SnakeGame.png new file mode 100644 index 000000000..96d72b38a Binary files /dev/null and b/tests/Images/Input/Png/SnakeGame.png differ diff --git a/tests/Images/Input/Png/banner7-adam.png b/tests/Images/Input/Png/banner7-adam.png new file mode 100644 index 000000000..a332f02e0 Binary files /dev/null and b/tests/Images/Input/Png/banner7-adam.png differ diff --git a/tests/Images/Input/Png/banner8-index.png b/tests/Images/Input/Png/banner8-index.png new file mode 100644 index 000000000..ee5d81cb2 Binary files /dev/null and b/tests/Images/Input/Png/banner8-index.png differ diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/blur.png b/tests/Images/Input/Png/blur.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/blur.png rename to tests/Images/Input/Png/blur.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png b/tests/Images/Input/Png/chunklength1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png rename to tests/Images/Input/Png/chunklength1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png b/tests/Images/Input/Png/chunklength2.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png rename to tests/Images/Input/Png/chunklength2.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png b/tests/Images/Input/Png/cross.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png rename to tests/Images/Input/Png/cross.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png b/tests/Images/Input/Png/filter0.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter0.png rename to tests/Images/Input/Png/filter0.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png b/tests/Images/Input/Png/filter1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter1.png rename to tests/Images/Input/Png/filter1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png b/tests/Images/Input/Png/filter2.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter2.png rename to tests/Images/Input/Png/filter2.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png b/tests/Images/Input/Png/filter3.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter3.png rename to tests/Images/Input/Png/filter3.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png b/tests/Images/Input/Png/filter4.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filter4.png rename to tests/Images/Input/Png/filter4.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png b/tests/Images/Input/Png/filterVar.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/filterVar.png rename to tests/Images/Input/Png/filterVar.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/indexed.png b/tests/Images/Input/Png/indexed.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/indexed.png rename to tests/Images/Input/Png/indexed.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png b/tests/Images/Input/Png/interlaced.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/interlaced.png rename to tests/Images/Input/Png/interlaced.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pd.png b/tests/Images/Input/Png/pd.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/pd.png rename to tests/Images/Input/Png/pd.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pl.png b/tests/Images/Input/Png/pl.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/pl.png rename to tests/Images/Input/Png/pl.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png b/tests/Images/Input/Png/pp.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/pp.png rename to tests/Images/Input/Png/pp.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp-interlaced.png b/tests/Images/Input/Png/rgb-48bpp-interlaced.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp-interlaced.png rename to tests/Images/Input/Png/rgb-48bpp-interlaced.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp.png b/tests/Images/Input/Png/rgb-48bpp.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/rgb-48bpp.png rename to tests/Images/Input/Png/rgb-48bpp.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/splash-interlaced.png b/tests/Images/Input/Png/splash-interlaced.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/splash-interlaced.png rename to tests/Images/Input/Png/splash-interlaced.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/splash.png b/tests/Images/Input/Png/splash.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/splash.png rename to tests/Images/Input/Png/splash.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_1.png b/tests/Images/Input/Png/versioning-1_1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_1.png rename to tests/Images/Input/Png/versioning-1_1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_2.png b/tests/Images/Input/Png/versioning-1_2.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/versioning-1_2.png rename to tests/Images/Input/Png/versioning-1_2.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_1.png b/tests/Images/Input/Png/vim16x16_1.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_1.png rename to tests/Images/Input/Png/vim16x16_1.png diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_2.png b/tests/Images/Input/Png/vim16x16_2.png similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Png/vim16x16_2.png rename to tests/Images/Input/Png/vim16x16_2.png