SkiaSharp
| Language | C# (.NET, .NET Framework, Xamarin/MAUI) |
|---|---|
| License | MIT (Skia itself: BSD-3-Clause) |
| Repository | github.com/mono/SkiaSharp |
| Documentation | SkiaSharp API reference |
| Depends on | native Skia binaries (NuGet packages per platform, included automatically on Windows and macOS) |
| Read / write | yes / yes; baseline and progressive decoding, baseline encoding with 4:2:0, 4:2:2 or 4:4:4 |
SkiaSharp wraps Skia, the 2D graphics engine of Chrome, Android and Flutter. Skia decodes JPEG with libjpeg-turbo, so decoding is fast, and it can decode directly to ½, ¼ or 1/8 size. It’s the usual choice for drawing, image resizing and rendering in .NET desktop and mobile apps.
SkiaSharp doesn’t parse Exif beyond the orientation (SKCodec.EncodedOrigin) and doesn’t copy metadata when encoding: re-encoded files lose Exif, XMP and the ICC profile. Use MetadataExtractor to read metadata.
Installation#
dotnet add package SkiaSharp
# Linux servers also need the native library package
dotnet add package SkiaSharp.NativeAssets.Linux
Basic usage: load, resize and save a JPEG#
using SkiaSharp;
using SKBitmap original = SKBitmap.Decode("in.jpg")
?? throw new Exception("not an image");
Console.WriteLine($"{original.Width} x {original.Height}");
int width = 800;
int height = original.Height * width / original.Width;
var sampling = new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.Linear);
using SKBitmap resized = original.Resize(new SKImageInfo(width, height), sampling);
using SKData data = resized.Encode(SKEncodedImageFormat.Jpeg, 85);
using FileStream output = File.OpenWrite("out.jpg");
data.SaveTo(output);
How to get JPEG size and orientation without decoding#
SKCodec reads the header only:
using SKCodec codec = SKCodec.Create("in.jpg");
SKImageInfo info = codec.Info;
Console.WriteLine($"{info.Width} x {info.Height}, origin {codec.EncodedOrigin}");
// EncodedOrigin: TopLeft (normal), RightTop (rotate 90° clockwise), BottomRight (180°), LeftBottom (90° counter-clockwise)
How to decode a JPEG at reduced size (fast thumbnails)#
The JPEG decoder can scale by 1/8 steps while decoding. Ask the codec for the nearest supported size:
using SKCodec codec = SKCodec.Create("big.jpg");
SKSizeI size = codec.GetScaledDimensions(0.25f); // about 1/4
var info = new SKImageInfo(size.Width, size.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
using SKBitmap thumb = SKBitmap.Decode(codec, info);
How to apply the Exif orientation#
SKBitmap.Decode returns pixels as stored. Rotate with a canvas when the origin isn’t TopLeft:
static SKBitmap Rotate90(SKBitmap src)
{
var dst = new SKBitmap(src.Height, src.Width);
using var canvas = new SKCanvas(dst);
canvas.Translate(dst.Width, 0);
canvas.RotateDegrees(90);
canvas.DrawBitmap(src, 0, 0);
return dst;
}
using SKCodec codec = SKCodec.Create("photo.jpg");
using SKBitmap bitmap = SKBitmap.Decode(codec);
using SKBitmap upright = codec.EncodedOrigin == SKEncodedOrigin.RightTop ? Rotate90(bitmap) : bitmap.Copy();
How to set chroma subsampling when encoding#
SKJpegEncoderOptions controls quality, subsampling and how alpha is handled:
using SKPixmap pixmap = bitmap.PeekPixels();
var options = new SKJpegEncoderOptions(92, SKJpegEncoderDownsample.Downsample444, SKJpegEncoderAlphaOption.Ignore);
using FileStream output = File.OpenWrite("out.jpg");
pixmap.Encode(output, options);
Tips#
Encodedoesn’t write progressive JPEG. For progressive output or metadata copying, use Magick.NET or ImageSharp.- Transparent pixels become black in JPEG. Draw the bitmap onto a canvas cleared with
SKColors.Whitebefore encoding. SKImage.FromEncodedDatakeeps the image lazily decoded and is cheaper when you only draw it once.