Search code examples
c#.net-coresystem.drawingskiasharpsystem.drawing.common

System.Drawing.Image to SkiaSharp


I am trying to move my code from using System.Drawing.Image to using SkiaSharp as recommended here.

I trying to find similar operations for working with Tif files from a stream.

Currently, the following is what I have using System.Drawing.Image:

System.Drawing.Image MyImage = System.Drawing.Image.FromStream(inStream);
PdfDocument doc = new PdfDocument();

for (int PageIndex = 0; PageIndex < MyImage.GetFrameCount(FrameDimension.Page); PageIndex++)
{
    MyImage.SelectActiveFrame(FrameDimension.Page, PageIndex);
    XImage img = XImage.FromGdiPlusImage(MyImage);
    var page = new PdfPage();

    page.Width = MyImage.Width;
    page.Height = MyImage.Height;
    doc.Pages.Add(page);
    XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[PageIndex]);
    xgr.DrawImage(img, 0, 0, page.Width, page.Height);
}

doc.Save(outStream);
MyImage.Dispose();

My current work with SkiaSharp is the following though it does not work correctly. Note: The following code has the following error and I am trying to figure out why in addition to how to select active frames: Unhandled exception. System.ObjectDisposedException: Cannot access a closed Stream. likely due to SKCodec codec = SkiaSharp.SKCodec.Create(inStream); but I am not sure why.

PdfDocument doc = new PdfDocument();
doc.Info.CreationDate = new DateTime();

using MemoryStream inStream = new MemoryStream(data);
using var imgStream = new SKManagedStream(inStream, false);
using var skData = SKData.Create(imgStream);
using SKCodec codec = SKCodec.Create(skData);

// TODO: codec is null!

for (int PageIndex = 0; PageIndex < codec.FrameCount; PageIndex++)
{
    SKImageInfo imageInfo = new SKImageInfo(codec.Info.Width, codec.Info.Height);

    // create bitmap stub
    using SKBitmap skBitmap = new SKBitmap(imageInfo);
    IntPtr pixelsPtr = skBitmap.GetPixels();

    // decode particular frame into the bitmap
    codec.GetPixels(imageInfo, pixelsPtr, new SKCodecOptions(PageIndex));

    // encode bitmap back
    using SKData encodedData = skBitmap.Encode(SKEncodedImageFormat.Png, 100);
    using Stream frameStream = encodedData.AsStream();

    XImage img = XImage.FromStream(frameStream);

    var page = new PdfPage();
    doc.Pages.Add(page);
    XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[PageIndex]);
    xgr.DrawImage(img, 0, 0, page.Width, page.Height);
}
doc.Save(destination);

These are other things I have tried:

  • Using SKCodecOptions to set the FrameIndex but how would I use it since it used for GetPixels() not Decode().
  • There are no properties, I found, that control FrameIndex within a SKBitmap.
  • The additional conversion from Image to XImage is a bit confusing as from the source here, it appears to be a constructor that creates an XImage straight from an Image. I would need to create an XImage from a SkiaSharp.SKBitmap instead I believe thought I'm not quite sure how to do this at the moment such as use an existing method like FromStream() though I don't know the differences between the uses necesarily.

Solution

  • You get the exception because decoding SKBitmap with

    SkiaSharp.SKBitmap MyImage = SkiaSharp.SKBitmap.Decode(inStream);
    

    disposes the inStream.

    Anyway, to get the particular frame using SkiaSharp, you need to get pixels from the codec:

    using MemoryStream inStream = new MemoryStream(data);
    PdfDocument doc = new PdfDocument();
    doc.Info.CreationDate = new DateTime();
    
    using SKCodec codec = SkiaSharp.SKCodec.Create(inStream);
    for (int PageIndex = 0; PageIndex < codec.FrameCount; PageIndex++)
    {
        SKImageInfo imageInfo = new SKImageInfo(codec.Info.Width, codec.Info.Height);
    
        // create bitmap stub
        using SKBitmap skBitmap = new SKBitmap(imageInfo);
        IntPtr pixelsPtr = skBitmap.GetPixels();
    
        // decode particular frame into the bitmap
        codec.GetPixels(imageInfo, pixelsPtr, new SKCodecOptions(PageIndex));
    
        // encode bitmap back
        using SKData encodedData = skBitmap.Encode(SKEncodedImageFormat.Png, 100);
        using Stream frameStream = encodedData.AsStream();
    
        XImage img = XImage.FromStream(frameStream);
    
        var page = new PdfPage();
    
        doc.Pages.Add(page);
        XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[PageIndex]);
        xgr.DrawImage(img, 0, 0, page.Width, page.Height);
    }
    doc.Save(destination);