Skip to content

Instantly share code, notes, and snippets.

@saucecontrol
Last active April 19, 2020 16:44
Show Gist options
  • Save saucecontrol/b1940571f7151a2d4b2822de587df71c to your computer and use it in GitHub Desktop.
Save saucecontrol/b1940571f7151a2d4b2822de587df71c to your computer and use it in GitHub Desktop.
MagicScaler with SkiaSharp on Linux
using System;
using System.IO;
using System.Drawing;
using System.Runtime.InteropServices;
using SkiaSharp;
using PhotoSauce.MagicScaler;
class Program
{
// Ideally an IPixelSource implementation would use Skia's ScanLine or Incremental decoding
// if supported by the codec. This one wraps a fully-decoded image.
private class SkiaPixelSource : BitmapPixelSource
{
private readonly byte[] pixels;
public SkiaPixelSource(Guid format, int width, int height, int stride, byte[] pixels)
: base(format, width, height, stride) => this.pixels = pixels;
protected override ReadOnlySpan<byte> Span => pixels.AsSpan();
}
public static void Main()
{
string inImgPath = "/mnt/c/img/bigimage.jpg";
string outImgPath = "/mnt/c/img/smallimage.jpg";
// Use SKCodec so we can control the details of the decoding.
using var skCodec = SKCodec.Create(inImgPath);
var skSrcInfo = skCodec.Info;
// Alternate SKImageInfo settings force the decoder to convert the pixel format on decode.
// This could be smarter about pixel format mapping. For now, we pick a format that is compatible between
// Skia and MagicScaler (SkiaSharp.SKColorType.Bgra8888 == PhotoSauce.MagicScaler.PixelFormats.Bgra32bpp)
// Since we can't retrieve an ICC profile directly from Skia, we ask it to convert to sRGB on decode.
// Note that this sample doesn't handle Exif Orientation, which is encoded in `SKCodec.EncodedOrigin`
var skDecodeInfo = new SKImageInfo {
Width = skSrcInfo.Width,
Height = skSrcInfo.Height,
ColorType = SKColorType.Bgra8888,
AlphaType = SKAlphaType.Unpremul,
ColorSpace = SKColorSpace.CreateSrgb()
};
// Create a buffer to hold the decoded pixels.
int inStride = skDecodeInfo.Width * skDecodeInfo.BytesPerPixel;
var inPixels = new byte[inStride * skDecodeInfo.Height];
var inHandle = GCHandle.Alloc(inPixels, GCHandleType.Pinned);
// Decode the input image into the buffer.
skCodec.GetPixels(skDecodeInfo, inHandle.AddrOfPinnedObject(), inStride, SKCodecOptions.Default);
inHandle.Free();
// Create an IPixelSource to wrap the buffer. The pixel format must be binary-compatible with the Skia pixel format.
using var psSource = new SkiaPixelSource(PixelFormats.Bgra32bpp, skDecodeInfo.Width, skDecodeInfo.Height, inStride, inPixels);
// Create a MagicScaler pipeline to resize the image, then retrieve its IPixelSource.
using var psPipeline = MagicImageProcessor.BuildPipeline(psSource, new ProcessImageSettings { Width = 400 });
var psOutput = psPipeline.PixelSource;
// Retrieve the target size from the pipeline. We keep the pixel format and color space from the source.
var skDstInfo = skDecodeInfo.WithSize(psOutput.Width, psOutput.Height);
// Create a buffer for the resized image.
int outStride = skDstInfo.Width * skDstInfo.BytesPerPixel;
var outPixels = new byte[outStride * skDstInfo.Height];
var outHandle = GCHandle.Alloc(outPixels, GCHandleType.Pinned);
// Resize the image into the buffer.
psOutput.CopyPixels(new Rectangle(0, 0, psOutput.Width, psOutput.Height), outStride, outPixels);
// Wrap the buffer in an SKImage.
using var skDstImage = SKImage.FromPixels(skDstInfo, outHandle.AddrOfPinnedObject(), outStride);
// And save the encoded output.
using var outFile = new FileStream(outImgPath, FileMode.Create);
skDstImage.Encode(SKEncodedImageFormat.Jpeg, psPipeline.Settings.JpegQuality).SaveTo(outFile);
outHandle.Free();
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/saucecontrol/PhotoSauce/_packaging/photosauce_ci/nuget/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PhotoSauce.MagicScaler" Version="0.10.4-ci*" />
<PackageReference Include="SkiaSharp" Version="1.68.1.1" />
<PackageReference Condition="$([MSBuild]::IsOSPlatform('linux'))" Include="SkiaSharp.NativeAssets.Linux" Version="1.68.1.1" />
</ItemGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment