PicReady 1.0.2

dotnet add package PicReady --version 1.0.2
                    
NuGet\Install-Package PicReady -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PicReady" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PicReady" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="PicReady" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PicReady --version 1.0.2
                    
#r "nuget: PicReady, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PicReady@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PicReady&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=PicReady&version=1.0.2
                    
Install as a Cake Tool

PicReady

PicReady turns an uploaded photo into a file you can store and serve: oriented upright, fitted to a box, cropped or masked, optionally watermarked, and encoded as JPEG, PNG, or WebP. PicReady.Lite is the free MIT subset for a plain resize.

Both packages target net8.0 and net9.0. Rendering is SkiaSharp only. There is no System.Drawing and no ImageSharp dependency.

The license key is emailed within about an hour of purchase. Activation is below.

Quick start

dotnet add package PicReady.Lite
using PicReady;

var thumb = LiteJob.Load(upload)
    .Resize(480, 480, FitMode.Contain)
    .Background(RgbColor.White)
    .Jpeg(JpegPreset.Medium)
    .ToBytes();

Pro:

dotnet add package PicReady
PicReadyLicense.Register(licenseKey);

var webp = PicJob.Load(upload)
    .Resize(960, 960, FitMode.Cover)
    .Webp(80)
    .ToBytes();

Pic.Process accepts a byte[], a Stream, or a file path. Every synchronous Pro method has an async twin with the same result. Streams are not disposed by the library. File output is written to a temporary file and moved into place.

Avatar upload, WebP under 100 KB

var avatar = PicJob.Load(upload)
    .Circle(256)
    .MaxBytes(100 * 1024, OutputFormat.Webp)
    .ToBytes();

Circle cover-fits the photo into a square and clears the pixels outside the circle. PNG and WebP keep that outside area transparent. JPEG paints AvatarBackground, and a transparent background becomes white. MaxBytes binary-searches quality and returns the highest quality that fits. If quality MinAutoQuality is still too big, it throws and names the budget and the smallest size it reached.

Product images at 320, 640, and 1280

var set = PicJob.Load(upload)
    .Png()
    .ResizeMany(320, 640, 1280);

One decode, then one encode per longest side, in the order you asked for. A crop set on the same job is applied before the responsive scale.

Strip camera EXIF

Orientation is applied by default, then metadata is dropped, so a phone photo is stored upright and a later viewer does not rotate it again.

var stored = Pic.Process(upload, new PicSettings
{
    Width = 1600,
    Fit = FitMode.MaxWidth,
    Format = OutputFormat.Jpeg,
    Quality = 85
});

var info = Pic.GetInfo(stored);
// info.HasExif is false. info.ExifOrientation is 1.

KeepMetadata = true copies the EXIF block (and PNG text chunks) and sets the orientation tag to 1 after the pixels have been rotated. Leave ApplyExifOrientation false only when you intentionally want the stored tag left as the camera wrote it.

ASP.NET Core

using PicReady.AspNetCore;

app.MapPost("/photos", async (IFormFile file) =>
{
    var settings = new PicSettings
    {
        Width = 1280,
        Fit = FitMode.MaxWidth,
        Format = OutputFormat.Webp,
        MaxBytes = 180 * 1024
    };
    var bytes = await PicFormFile.ProcessAsync(file, settings);
    return Results.File(bytes, PicContent.GetContentType(settings.Format));
});

An empty upload throws. The content type comes from the bytes you asked to encode, not from the filename. PicContent.TryDetect and ImageContentTypes.TryDetect sniff JPEG, PNG, GIF, and WebP headers.

Batch a folder

var files = Directory.GetFiles(inbox, "*.jpg");
var results = await Pic.BatchProcessAsync(
    files,
    path => Path.Combine(outbox, Path.GetFileNameWithoutExtension(path) + ".webp"),
    new PicSettings { Width = 1600, Fit = FitMode.MaxWidth, Format = OutputFormat.Webp, Quality = 80 },
    maxDegreeOfParallelism: 4);

foreach (var item in results.Where(item => !item.Success))
    Console.Error.WriteLine($"{item.SourcePath}: {item.Error}");

A bad file is reported on that index. The rest of the batch continues. Cancellation stops scheduling further work. maxDegreeOfParallelism below 1 throws.

var marked = PicJob.Load(upload)
    .Resize(1200, 1200, FitMode.MaxWidth)
    .Watermark("Northwind", mark =>
    {
        mark.Gravity = Gravity.Bottom;
        mark.Margin = 16;
        mark.Opacity = 0.85f;
        mark.Color = RgbColor.White;
    })
    .Jpeg(85)
    .ToBytes();

Text and image marks are drawn on the finished canvas. They do not change the width or height. Opacity 0 leaves the pixels unchanged. A mark larger than the photo is scaled down so it stays inside the margin.

Contain, Cover, and Stretch

Mode Result
Contain The whole image is inside the box. The leftover area is Background. Nothing is cropped.
Cover The box is filled. Overflow is cropped using Gravity or the focal point. There is no letterbox.
Stretch The image is scaled to the exact box. Aspect ratio is not kept.
MaxWidth Width becomes the requested width. Height follows the aspect ratio, including upscale.
MaxHeight Height becomes the requested height. Width follows.

MaxLongestSide clamps the output after the fit. A 4000px photo is still decoded (within the safety caps) and then scaled down. It is not rejected just because it is larger than the clamp.

Docker and Linux

The packages reference SkiaSharp.NativeAssets.Linux.NoDependencies, plus the Windows and macOS asset packages, so a normal dotnet restore on Linux pulls a native build that does not need a system libfontconfig. The DejaVu Sans font used for watermarks and the trial mark is embedded.

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "YourApp.dll"]

Publish with dotnet publish -c Release. You do not need apt-get install fontconfig for the built-in text path.

Lite and Pro

PicReady.Lite PicReady
License MIT Commercial EULA, offline key
JPEG, PNG, GIF decode Yes, GIF is the first frame Yes, plus WebP
Encode JPEG, PNG JPEG, PNG, WebP
Fit modes Contain, Cover, Stretch, MaxWidth, MaxHeight Same
JPEG quality Presets 60, 75, 90 1–100, plus MaxBytes search
EXIF orient and strip No Yes
Crop, focal, rotate, flip No Yes
Circle and rounded-rect avatars No Yes
Text and image watermarks No Yes
ResizeMany, batch, async, IFormFile No Yes

Lite JPEG preset 75 matches a licensed Pro JPEG at quality 75 for the same PNG source and the same fit. WebP input or an unknown Lite format throws an exception whose message contains "Pro". Leaving Lite width and height at 0 re-encodes at the original size, the same as omitting Pro width and height.

Options

LiteSettings: Width, Height, Fit, Background, Format, Quality, MaxLongestSide, MaxPixels, MaxInputBytes.

PicSettings adds CropWidth, CropHeight, Gravity, FocalX, FocalY, RotateDegrees, Flip, Avatar, AvatarSize, CornerRadius, AvatarBackground, ApplyExifOrientation, KeepMetadata, TextWatermark, ImageWatermark, MaxBytes, MinAutoQuality, MaxAutoQuality.

TextWatermark: Text, Gravity, Opacity, Margin, FontSize, Color.

ImageWatermark: Image, Gravity, Opacity, Margin.

Operation order: decode, EXIF orientation, rotate, flip, crop, avatar mask, resize, image watermark, text watermark, trial mark, encode.

Hard safety caps, applied before Skia allocates a decoded bitmap: longest side 16384, 80 million pixels, 200 MB input. Defaults are 8192, 40 million pixels, and 80 MB. Raising a setting never goes past the hard cap. A header over the cap throws a managed PicException.

Licensing

Buy a Developer key ($19/year per developer, 1–50 seats) or a Team key ($79/year, up to 10 developers). The key is emailed within about an hour of purchase.

Activate it in any one of these places. The first value that is present wins:

  1. Call PicReadyLicense.Register(key) at startup.
  2. Set the PICREADY_LICENSE_KEY environment variable to the token.
  3. Put the token in a picready.lic file next to the application.
  4. Put the token in license.key under the PicReady application-data folder. PICREADY_APPDATA overrides that folder.
PicReadyLicense.Register(Environment.GetEnvironmentVariable("PICREADY_LICENSE_KEY"));

PicReadyLicense.Register validates the key immediately and throws PicLicenseException when the key is invalid, expired, tampered, or for the wrong product. If a valid key is already active, that key stays in place. If no valid key is active, the failure is latched: later processing throws PicLicenseException instead of producing a trial image, until a valid key is registered. A key that is present in PICREADY_LICENSE_KEY, picready.lic, or license.key and is invalid does not fall through to a later location or to the trial. The key is checked offline. The package does not phone home.

Trial

With no key loaded, and when Register has not rejected a key, Pro still runs.

  • Output is a valid JPEG, PNG, or WebP of the same dimensions, with a small corner mark that reads "PicReady trial". The mark does not replace the photo.
  • Outside Development, the first Pro call writes a stamp file and starts a 14-day window. PicReadyLicense.Notice reports the days remaining. At the instant that window ends, processing throws PicLicenseException and LicenseStatus.OutputAllowed is false.
  • Development does not expire. Set ASPNETCORE_ENVIRONMENT or DOTNET_ENVIRONMENT to Development.
  • PicReadyLicense.Register with a valid unexpired key removes the mark. PicReadyLicense.Status then reports LicenseMode.Licensed.

Comparison

PicReady ImageSharp SkiaSharp Magick.NET
Price Developer $19/yr per developer (1–50 seats). Team $79/yr, up to 10 developers ImageSharp Boutique is listed at $799/year for up to 10 developers MIT Apache-2.0
License check Offline key, or a visible trial mark ImageSharp 3.2 / 4.x documents a build-time license key None None
Scope Upload to a served image General imaging Low-level drawing ImageMagick operations
Native bits Skia via NuGet assets Managed You choose the asset package ImageMagick native dependencies

Sources for the ImageSharp figures: sixlabors.com/pricing and the licence enforcement post. PicReady is not a drop-in ImageSharp replacement. It does not offer ImageSharp's pixel API, and it does not aim to.

Measured speed

Stopwatch, Release build, one warmup plus five iterations, median. Source 1280×720 noise, output 640×360 Contain. Machine cursor, 4 processors. These are the numbers from that run, not a laptop claim and not a BenchmarkDotNet suite.

.NET 8.0.33 .NET 9.0.22
Decode, resize, JPEG quality 80 46.4 ms 48.0 ms
Decode, resize, WebP quality 80 74.3 ms 76.8 ms
Same JPEG with MaxBytes 80 KB 135.7 ms 134.3 ms

MaxBytes encodes several qualities. It is slower than a single encode on purpose.

Limits

  • GIF and animated WebP: the first frame is decoded. GIF cannot be encoded.
  • CMYK JPEG is decoded to RGB. There is no CMYK output.
  • JPEG metadata keep copies the EXIF TIFF block. Other APP segments (a separate XMP packet, IPTC, ICC) are not copied. PNG keep copies tEXt, zTXt, iTXt, and eXIf. WebP keep copies the EXIF chunk.
  • WebP encode is lossy. There is no lossless WebP switch.
  • Angles other than 90, 180, and 270 grow the canvas and resample. Right angles are pixel copies.
  • Seat counts are in the key and in the EULA. The library does not count running developers.
  • HEIC, AVIF, and TIFF are rejected.

Build

dotnet test PicReady.sln -c Release
dotnet pack src/PicReady.Lite/PicReady.Lite.csproj -c Release
dotnet pack src/PicReady/PicReady.csproj -c Release
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 0 11/9/2026