Skip to content

The official .NET API for Imageflow, the Rust image processing and optimization engine for web servers

License

Notifications You must be signed in to change notification settings

imazen/imageflow-dotnet

Repository files navigation

Build status

Imageflow.NET is a .NET API for Imageflow, the fast image optimization and processing library for web servers. Imageflow focuses on security, quality, and performance - in that order. Imageflow.NET is a .NET 8.0 & .NET Standard 2.0 library, and as such is compatible with .NET 4.6.2+, .NET Core 2.0+, and .NET 5/6/7/8/9.

Note: We recently switched from Newtonsoft to System.Text.Json to support AOT and trimming; see CHANGES.md for details and some breaking changes. There are also new classes for attaching source image data to jobs; use MemorySource.* over ByteSource and BufferedStreamSource.* instead of StreamSource.

On .NET Core 3.x and .NET 5/6/7/8 (or if using PackageReference on .NET 4.x)

dotnet add package Imageflow.AllPlatforms

If you're still using packages.config on .NET 4.x (such as for ASP.NET projects), you have to install Imageflow directly

PM> Install-Package Imageflow.Net
PM> Install-Package Imageflow.NativeRuntime.win-x86 -pre
PM> Install-Package Imageflow.NativeRuntime.win-x86_64 -pre
PM> Install-Package Imageflow.NativeRuntime.osx-x86_64 -pre
PM> Install-Package Imageflow.NativeRuntime.ubuntu-x86_64 -pre

Note: On .NET 4.x you must install the appropriate NativeRuntime(s) in the project you are deploying - they have to copy imageflow.dll to the output folder. They are not copied transitively.

Also note: Older versions of Windows may not have the C Runtime installed (Install 32-bit or 64-bit).

License

  • Imageflow is dual licensed under a commercial license and the AGPLv3.
  • Imageflow.NET is tri-licensed under a commercial license, the AGPLv3, and the Apache 2 license.
  • Imageflow.NET Server is dual licensed under a commercial license and the AGPLv3.
  • We offer commercial licenses at https://imageresizing.net/pricing
  • Imageflow.NET's Apache 2 license allows for integration with non-copyleft products, as long as jobs are not actually executed (since the AGPLv3/commercial license is needed when libimageflow is linked at runtime). This can allow end-users to benefit from optional imageflow integration in products.

Examples

Getting image dimensions and format

using Imageflow.Fluent;

public async void TestGetImageInfo()
{
    var imageBytes = Convert.FromBase64String(
        "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=");

    var info = await ImageJob.GetImageInfo(new MemorySource(imageBytes));
    
    Assert.Equal(info.ImageWidth, 1);
    Assert.Equal(info.ImageHeight, 1);
    Assert.Equal(info.PreferredExtension, "png");
    Assert.Equal(info.PreferredMimeType, "image/png");
}

Edit images with the fluent API

using Imageflow.Fluent;
public async Task TestAllJob()
{
    var imageBytes = Convert.FromBase64String(
        "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=");
    using (var b = new ImageJob())
    {
        var r = await b.Decode(imageBytes)
            .FlipVertical()
            .FlipHorizontal()
            .Rotate90()
            .Rotate180()
            .Rotate270()
            .Transpose()
            .CropWhitespace(80, 0.5f)
            .Distort(30, 20)
            .Crop(0,0,10,10)
            .Region(-5,-5,10,10, AnyColor.Black)
            .RegionPercent(-10f, -10f, 110f, 110f, AnyColor.Transparent)    
            .BrightnessSrgb(-1f)
            .ContrastSrgb(1f)
            .SaturationSrgb(1f)
            .WhiteBalanceSrgb(80)
            .ColorFilterSrgb(ColorFilterSrgb.Invert)
            .ColorFilterSrgb(ColorFilterSrgb.Sepia)
            .ColorFilterSrgb(ColorFilterSrgb.Grayscale_Bt709)
            .ColorFilterSrgb(ColorFilterSrgb.Grayscale_Flat)
            .ColorFilterSrgb(ColorFilterSrgb.Grayscale_Ntsc)
            .ColorFilterSrgb(ColorFilterSrgb.Grayscale_Ry)
            .ExpandCanvas(5,5,5,5,AnyColor.FromHexSrgb("FFEECCFF"))
            .FillRectangle(2,2,8,8, AnyColor.Black)
            .ResizerCommands("width=10&height=10&mode=crop")
            .ConstrainWithin(5, 5)
            .Watermark(new BytesSource(imageBytes), 
                new WatermarkOptions()
                   .SetMarginsLayout(
                        new WatermarkMargins(WatermarkAlign.Image, 1,1,1,1), 
                        WatermarkConstraintMode.Within, 
                        new ConstraintGravity(90,90))
                    .SetOpacity(0.5f)
                    .SetHints(new ResampleHints().SetSharpen(15f, SharpenWhen.Always))
                    .SetMinCanvasSize(1,1))
            .EncodeToBytes(new MozJpegEncoder(80,true))
            .Finish().InProcessAsync();

        Assert.Equal(5, r.First.Width);
        Assert.True(r.First.TryGetBytes().HasValue);
    }
}

Generate multiple versions of one image

using Imageflow.Fluent;
public async Task TestMultipleOutputs()
{
    var imageBytes = Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=");
    using (var b = new ImageJob())
    {
        var r = await b.Decode(imageBytes).
            Constrain(new Constraint(ConstraintMode.Fit, 160, 120))
            .Branch(f => f.ConstrainWithin(80, 60).EncodeToBytes(new WebPLosslessEncoder()))
            .Branch(f => f.ConstrainWithin(40, 30).EncodeToBytes(new WebPLossyEncoder(50)))
            .EncodeToBytes(new LodePngEncoder())
            .Finish().InProcessAsync();

        Assert.Equal(60, r.TryGet(1).Width);
        Assert.Equal(30, r.TryGet(2).Width);
        Assert.Equal(120, r.TryGet(3).Width);
        Assert.True(r.First.TryGetBytes().HasValue);
    }

}

Edit images using a command string

using Imageflow.Fluent;

public async Task TestBuildCommandString()
{
    var imageBytes = Convert.FromBase64String(
        "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=");
    // We wrap the job in a using() statement to free memory faster
    using (var b = new ImageJob())
    {
        
        var r = await b.BuildCommandString(
            new MemorySource(imageBytes), // or new StreamSource(Stream stream, bool disposeStream)
            new BytesDestination(), // or new StreamDestination
            "width=3&height=2&mode=stretch&scale=both&format=webp&webp.quality=80")
            .Finish().InProcessAsync();

        Assert.Equal(3, r.First.Width);
        Assert.Equal("webp", r.First.PreferredExtension);
        Assert.True(r.First.TryGetBytes().HasValue);
    }
}