Search code examples
c#.net.net-coreskiasharpskia

Extracting image subset (cropping) with SkiaSharp


I am trying to re-purpose an example from GitHub that deals with cropping an image with SkiaSharp. Specifically, I have a 4096x4096 sprite sheet from which I'd like to extract a sub-image (a specific sprite, if you will). To do that, I use the following snippet (where spriteContent is a byte array of the PNG image - byte[]):

var gch = GCHandle.Alloc(spriteContent, GCHandleType.Pinned);
try
{
    var addr = gch.AddrOfPinnedObject();
    using var pixmap = new SkiaSharp.SKPixmap(info, addr);
    SkiaSharp.SKRectI rectI = new SkiaSharp.SKRectI(0, 0, 256, 256);

    var subset = pixmap.ExtractSubset(rectI);

    using var data = subset.Encode(SkiaSharp.SKPngEncoderOptions.Default)

    File.WriteAllBytes("test2.png", data.ToArray());
}
finally
{
    gch.Free();
}

The output of this code is, however, this kind of image:

Malformed PNG image

Seems like an odd output. I suspect that I am doing something funky with teh SKRectI declaration, where the true rectangle is never used. What I understand it to be doing is create a rectangle from point 0 on top, 0 on bottom, 256 pixels tall, 256 pixels wide (i.e., manage the selection). If I adjust this to, let's say:

SkiaSharp.SKRectI rectI = new SkiaSharp.SKRectI(256, 256, 256, 256);

I get a NullReferenceException and there is nothing in the subset, so I must be misinterpreting how the rectangle selector works.

Any thoughts on what I might be doing wrong here?


Solution

  • The noise you get is caused by the fact, that you reinterpret raw encoded png bytes as pixel data. You need to decode the image first:

    using var skBitmap = SKBitmap.Decode(spriteContent);
    
    using var pixmap =  new SKPixmap(skBitmap.Info, skBitmap.GetPixels());
    SkiaSharp.SKRectI rectI = new SkiaSharp.SKRectI(0, 0, 256, 256);
    
    var subset = pixmap.ExtractSubset(rectI);
    
    using var data = subset.Encode(SkiaSharp.SKPngEncoderOptions.Default);
    
    File.WriteAllBytes(@"test2.png", data.ToArray());