Home

Awesome

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

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);
    }
}

More examples are in the tests