Skip to content

Commit

Permalink
improve packaging and linux compat
Browse files Browse the repository at this point in the history
  • Loading branch information
saucecontrol committed Apr 17, 2022
1 parent 507914d commit 4f4c4f0
Show file tree
Hide file tree
Showing 20 changed files with 397 additions and 366 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Expand Up @@ -15,6 +15,7 @@ file_header_template = Copyright © Clinton Ingram and Contributors. Licensed u
csharp_style_deconstructed_variable_declaration = true:silent

dotnet_diagnostic.CA1031.severity = silent
dotnet_diagnostic.CA1816.severity = silent
dotnet_diagnostic.CS3016.severity = silent

dotnet_naming_rule.private_non_field_members_should_be_camel_case.severity = suggestion
Expand Down
34 changes: 34 additions & 0 deletions build/Branding.props
@@ -0,0 +1,34 @@
<Project>

<PropertyGroup>
<Title>High-Quality, High-Performance Image Processing for .NET</Title>
</PropertyGroup>

<Choose>
<When Condition="'$(MSBuildProjectName)'=='MagicScaler'">
<PropertyGroup>
<Description>High-Performance image processing pipeline for .NET. Implements best-of-breed algorithms, linear light processing, and sharpening for the best image resizing quality available. Speed, efficiency, and image quality are unmatched by anything else on the .NET platform.</Description>
<PackageTags>Image Resize Resample Process Crop Sharpen Rotate Flip WIC JPEG JPG PNG GIF TIFF</PackageTags>
</PropertyGroup>
</When>
<When Condition="'$(MSBuildProjectName)'=='WebRSize'">
<PropertyGroup>
<Description>Web extensions for the MagicScaler library, inlcluding an HTTP request intercept module for automatic image processing.</Description>
<PackageTags>ASP.NET Web Handler Module VirtualPathProvider Image Resize Resample Process</PackageTags>
</PropertyGroup>
</When>
<When Condition="'$(MSBuildProjectName)'=='NativeCodecs.Libheif'">
<PropertyGroup>
<Description>HEIC codec plugin for the PhotoSauce.MagicScaler pipeline.</Description>
<PackageTags>Image Decode HEIC HEIF</PackageTags>
</PropertyGroup>
</When>
<When Condition="'$(MSBuildProjectName)'=='NativeCodecs.Libjxl'">
<PropertyGroup>
<Description>JPEG XL codec plugin for the PhotoSauce.MagicScaler pipeline.</Description>
<PackageTags>Image Decode Encode JPEG-XL JXL</PackageTags>
</PropertyGroup>
</When>
</Choose>

</Project>
15 changes: 8 additions & 7 deletions build/Common.props
Expand Up @@ -5,18 +5,16 @@
<Authors>Clinton Ingram</Authors>
<RepositoryUrl>https://github.com/saucecontrol/PhotoSauce</RepositoryUrl>
<Copyright>Copyright © 2015-$([System.DateTime]::Today.Year) $(Authors)</Copyright>
<Title>High-Quality, High-Performance Image Processing for .NET</Title>
<Description>High-Quality, High-Performance Image Processing for .NET</Description>

<RootNamespace>$(Company).$(MSBuildProjectName)</RootNamespace>
<AssemblyName>$(Company).$(MSBuildProjectName)</AssemblyName>
<AssemblyTitle>$(MSBuildProjectName)</AssemblyTitle>
<Product>$(MSBuildProjectName)</Product>

<PackageIcon>package/$(Company).png</PackageIcon>
<PackageIcon>$(Company).png</PackageIcon>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://photosauce.net</PackageProjectUrl>
<PackageTags>Image Resize Resample Process Crop Sharpen Rotate Flip WIC JPEG JPG PNG GIF TIFF</PackageTags>
<PackageReleaseNotes>See $(RepositoryUrl)/releases for release-specific notes.</PackageReleaseNotes>

<LangVersion>preview</LangVersion>
Expand All @@ -27,6 +25,7 @@
<Configurations>Debug;Release;Dist</Configurations>
<Configuration Condition="'$(Configuration)'==''">Release</Configuration>

<ProjectRoot>$(MSBuildProjectDirectory)\</ProjectRoot>
<RepositoryRoot>$(MSBuildThisFileDirectory)..\</RepositoryRoot>
<BaseModulesPath>$(RepositoryRoot)modules\</BaseModulesPath>
<BaseIntermediateOutputPath>$(RepositoryRoot)out\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
Expand All @@ -50,16 +49,18 @@
<PropertyGroup Condition="'$(Configuration)'=='Dist' Or '$(Configuration)'=='Coverage'">
<Deterministic>true</Deterministic>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!--<IsTrimmable>true</IsTrimmable> https://github.com/dotnet/linker/issues/2622 -->
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="4.2.0-2.final" PrivateAssets="all" />
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'=='Dist' Or '$(Configuration)'=='Coverage'">
<None Include="$(MSBuildThisFileDirectory)$(Company).png" Pack="true" PackagePath="package" />
<None Include="$(RepositoryRoot)license" Pack="true" PackagePath="package" />
<None Include="$(MSBuildThisFileDirectory)$(Company).png" Pack="true" PackagePath="/" />
<None Include="$(ProjectRoot)readme.md" Pack="true" PackagePath="/" />
<None Include="$(RepositoryRoot)license" Pack="true" PackagePath="/" />
<None Include="$(RepositoryRoot)third-party-notices" Pack="true" PackagePath="/" />
<SourceRoot Include="$(RepositoryRoot)" />
</ItemGroup>

Expand Down
4 changes: 4 additions & 0 deletions building.md
Expand Up @@ -23,3 +23,7 @@ The projects can then be built as normal with Visual Studio or dotnet CLI. The
```
dotnet build src\MagicScaler -c Dist
```

Native codec binaries are built with [vcpkg](https://github.com/microsoft/vcpkg). See `azure-pipelines.yml` in the repo root for a working setup.
Alternatively, pre-built native binaries can be retrieved from a [recent CI run](https://dev.azure.com/saucecontrol/PhotoSauce/_build?definitionId=1) and extracted to `[reporoot]\out\vcpkg\install`
Once native binaries are built or downloaded, the native codec plugin packages can be built as normal with `dotnet build`
1 change: 1 addition & 0 deletions src/Directory.Build.props
@@ -1,6 +1,7 @@
<Project>

<Import Project="$(MSBuildThisFileDirectory)..\build\Common.props" />
<Import Project="$(MSBuildThisFileDirectory)..\build\Branding.props" />
<Import Project="$(MSBuildThisFileDirectory)..\build\FrameworkVersions.props" />

<PropertyGroup>
Expand Down
16 changes: 15 additions & 1 deletion src/Directory.Build.targets
@@ -1,6 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<!-- Work around https://github.com/Microsoft/msbuild/issues/3412 by writing the non-string assembly attributes manually -->
<!-- WriteCodeFragment task can write assembly attributes but not module attributes, so they are all handled here -->
<Target Name="_AddNonStringAssemblyInfoAttributes" AfterTargets="CoreGenerateAssemblyInfo" Outputs="$(AssemblyInfoFile)">
<ItemGroup>
<AssemblyInfoLines Include="[assembly:System.CLSCompliant(true)]" />
Expand All @@ -16,4 +16,18 @@
</ItemGroup>
</Target>

<!-- https://github.com/NuGet/Home/issues/5556 -->
<Target Name="_ExactProjectReferencesVersion" AfterTargets="_GetProjectReferenceVersions" Condition="'@(_ProjectReferencesWithVersions)'!=''">
<ItemGroup>
<_ProjectReferencesWithExactVersions Include="@(_ProjectReferencesWithVersions)">
<ProjectVersion>[%(_ProjectReferencesWithVersions.ProjectVersion),$([MSBuild]::Add($([System.Text.RegularExpressions.Regex]::Match('%(_ProjectReferencesWithVersions.ProjectVersion)', '^\d+\.\d+').Value), '0.01')))</ProjectVersion>
</_ProjectReferencesWithExactVersions>
</ItemGroup>

<ItemGroup>
<_ProjectReferencesWithVersions Remove="@(_ProjectReferencesWithVersions)" />
<_ProjectReferencesWithVersions Include="@(_ProjectReferencesWithExactVersions)" />
</ItemGroup>
</Target>

</Project>
2 changes: 1 addition & 1 deletion src/MagicScaler/Core/ProcessImageResult.cs
Expand Up @@ -117,7 +117,7 @@ internal ProcessingPipeline(PipelineContext ctx)
Context = ctx;
source = new Lazy<IPixelSource>(() => {
MagicTransforms.AddExternalFormatConverter(Context, true);
WicTransforms.AddPixelFormatConverter(Context, false);
MagicTransforms.AddNormalizingFormatConverter(Context, true);
return Context.Source;
});
Expand Down
54 changes: 47 additions & 7 deletions src/MagicScaler/Magic/MagicTransforms.cs
Expand Up @@ -138,6 +138,44 @@ public static void AddExternalFormatConverter(PipelineContext ctx, bool lastChan
ctx.Source = ctx.AddProfiler(new ConversionTransform(ctx.Source, PixelFormat.Bgr24));
}

public static void AddNormalizingFormatConverter(PipelineContext ctx, bool lastChance = false)
{
var curFormat = ctx.Source.Format;
if (curFormat.ColorRepresentation == PixelColorRepresentation.Cmyk)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
throw new PlatformNotSupportedException("CMYK conversion is not yet supported on this platform.");

WicTransforms.AddPixelFormatConverter(ctx);
curFormat = ctx.Source.Format;
}

if (curFormat == PixelFormat.Y8 || curFormat == PixelFormat.Cb8 || curFormat == PixelFormat.Cr8)
return;

var newFormat = PixelFormat.Bgr24;
if (!lastChance && curFormat.AlphaRepresentation == PixelAlphaRepresentation.Associated && ctx.Settings.BlendingMode != GammaMode.Linear && ctx.Settings.MatteColor.IsEmpty)
newFormat = PixelFormat.Pbgra32;
else if (curFormat.AlphaRepresentation != PixelAlphaRepresentation.None)
newFormat = PixelFormat.Bgra32;
else if (curFormat.ColorRepresentation == PixelColorRepresentation.Grey)
newFormat = PixelFormat.Grey8;

if (curFormat == newFormat)
return;

if ((curFormat == PixelFormat.Rgb24 && newFormat == PixelFormat.Bgr24) || (curFormat == PixelFormat.Rgba32 && curFormat == PixelFormat.Bgra32))
{
ctx.Source = ctx.AddProfiler(new ConversionTransform(ctx.Source, newFormat));
return;
}

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
throw new PlatformNotSupportedException($"[{curFormat.Name}]->[{newFormat.Name}] conversion is not yet implemented on this platform.");

WicTransforms.AddPixelFormatConverter(ctx, !lastChance);
}

public static void AddHighQualityScaler(PipelineContext ctx, ChromaSubsampleMode subsample = ChromaSubsampleMode.Subsample444)
{
bool swap = ctx.Orientation.SwapsDimensions();
Expand Down Expand Up @@ -368,15 +406,17 @@ public static unsafe void AddColorspaceConverter(PipelineContext ctx)

if (ctx.SourceColorProfile.ProfileType > ColorProfileType.Matrix || ctx.DestColorProfile.ProfileType > ColorProfileType.Matrix)
{
AddExternalFormatConverter(ctx);

if (ctx.WicContext.SourceColorContext is null)
ctx.WicContext.SourceColorContext = WicColorProfile.CreateContextFromProfile(ctx.SourceColorProfile.ProfileBytes);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (ctx.WicContext.SourceColorContext is null)
ctx.WicContext.SourceColorContext = WicColorProfile.CreateContextFromProfile(ctx.SourceColorProfile.ProfileBytes);

if (ctx.WicContext.DestColorContext is null)
ctx.WicContext.DestColorContext = WicColorProfile.CreateContextFromProfile(ctx.DestColorProfile.ProfileBytes);
if (ctx.WicContext.DestColorContext is null)
ctx.WicContext.DestColorContext = WicColorProfile.CreateContextFromProfile(ctx.DestColorProfile.ProfileBytes);

WicTransforms.AddColorspaceConverter(ctx);
AddExternalFormatConverter(ctx);
WicTransforms.AddColorspaceConverter(ctx);
}

return;
}
Expand Down
5 changes: 3 additions & 2 deletions src/MagicScaler/MagicScaler.csproj
Expand Up @@ -22,14 +22,15 @@
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.3" />
<PackageReference Include="IndexRange" Version="1.0.2" />
</ItemGroup>

<ItemGroup Condition="$(DefineConstants.Contains('GDIPROCESSOR')) And !$(DefineConstants.Contains('NETFRAMEWORK'))">
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
<PackageReference Include="System.Drawing.Common" Version="5.0.3" />
</ItemGroup>

<ItemGroup Condition="$(DefineConstants.Contains('BUILTIN_SPAN')) And $(DefineConstants.Contains('NETSTANDARD'))">
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>

<ItemGroup Condition="$(DefineConstants.Contains('NETFRAMEWORK')) And $([MSBuild]::VersionLessThan($(NetFrameworkVersion), '4.7'))">
Expand Down
30 changes: 30 additions & 0 deletions src/MagicScaler/readme.md
@@ -0,0 +1,30 @@
PhotoSauce.MagicScaler
======================

MagicScaler is a high-performance image processing pipeline for .NET, focused on making complex imaging tasks simple.

It implements best-of-breed algorithms, linear light processing, and sharpening for the best image resizing quality available.

Speed and efficiency are unmatched by anything else on the .NET platform.

Requirements
------------

MagicScaler currently has full functionality only on Windows. Although MagicScaler is compatible with -- and optimized for -- .NET Core and .NET 5+, it requires the [Windows Imaging Component](https://docs.microsoft.com/en-us/windows/desktop/wic/-wic-about-windows-imaging-codec) for its image codec support.

Work is in progress to reach full feature parity on Linux.

Usage
-----

### Image Resizing

```C#
MagicImageProcessor.ProcessImage(@"\img\big.jpg", @"\img\small.jpg", new ProcessImageSettings { Width = 400 });
```

The above example will resize `big.jpg` to a width of 400 pixels and save the output to `small.jpg`. The height will be set automatically to preserve the correct aspect ratio. Default settings are optimized for a balance of speed and image quality.

The MagicScaler pipleline is also customizable if you wish to use an alternate pixel source, capture the output pixels for additional processing, or add custom filtering.

See the [full documentation](https://docs.photosauce.net) for more details.
39 changes: 39 additions & 0 deletions src/ManagedCodecs/ImageSharp/readme.md
@@ -0,0 +1,39 @@
PhotoSauce.ManagedCodecs.ImageSharp
===================================

This MagicScaler plugin sample makes the [ImageSharp](https://github.com/SixLabors/ImageSharp) [TARGA](https://en.wikipedia.org/wiki/Truevision_TGA) codec available for decode and encode.

This sample presents the minimum implementation required to decode and encode for a codec with no metadata support.

Usage
-----

### Codec Registration

To register the codec, call the `UseImageSharpTga` extension method from your `CodecManager.Configure` action at app startup.

```C#
using PhotoSauce.MagicScaler;
using PhotoSauce.ManagedCodecs.ImageSharp;

CodecManager.Configure(codecs => {
codecs.UseImageSharpTga();
});
```

### Using the Codec

Once registered, the codec will automatically detect and decode compatible images.

To encode TGA images, the encoder MIME type can be set on `ProcessImageSettings`:

```C#
var settings = new ProcessImageSettings();
settings.TrySetEncoderFormat("image/tga")
```

Or the encoder can be selected by file extension on overloads accepting file paths:

```C#
MagicImageProcessor.ProcessImage(@"\img\input.jpg", @"\img\output.tga", settings);
```

0 comments on commit 4f4c4f0

Please sign in to comment.