Bug on SKCodec Dispose?

Hi,
I'm trying to use SKCodec, in order to rotate an image (using SKCodec.Origin etc..).

It works with only ONE IMAGE!

When I try to use the same code in a 'foreach' (who scans a directory containing many images)
it doesn't work more and the program crashes!!

It seems a problem of memory.

Is it maybe a bug??

Thanks a lot,
Paola.

Tagged:

Answers

  • paolapassarella5710paolapassarella5710 Member ✭✭
    edited May 18

    Here is the code source:

    foreach (string fileName in fileEntries)
    {
                   using (Stream input = File.OpenRead(fileName))
                    {
                        using (var inputStream = new SKManagedStream(input))
                        {
                          using (var codec = SKCodec.Create(inputStream))
                           {
                               SKCodecOrigin orientation = codec.Origin;
    
                               var inputStream2 = new SKManagedStream(input);
                               inputStream.Position = 0;
                               SKBitmap bitimage = GetBitmapRotated(inputStream, orientation);
    
                                SKImage image = SKImage.FromBitmap(bitimage);
                                nomefile = System.IO.Path.GetFileName(fileName);
    
                               Pathfileoutsk = System.IO.Path.Combine(@"D:\\Temp\\output\\", "R_SK_" + nomefile);
                                Stream str = File.OpenWrite(Pathfileoutsk);
                                using (var data = image.Encode(SKEncodedImageFormat.Jpeg, quality))
                                {
                                    data.SaveTo(str);
                                    str.Close();
                                }    
    
                 }
            }
               }
     }
    
    
    public static SKBitmap GetBitmapRotated(SKManagedStream _mgdStream, SKCodecOrigin orientation)
            {
                SKBitmap bitmap = SKBitmap.Decode(_mgdStream);
                SKBitmap rotated;
                switch (orientation)
                {
                    case SKCodecOrigin.BottomRight:
                        rotated = new SKBitmap(bitmap.Width, bitmap.Height);
    
                        using (SKCanvas surface = new SKCanvas(rotated))
                        {
                            surface.RotateDegrees(180, rotated.Width / 2, rotated.Height / 2);
                            surface.DrawBitmap(bitmap, 0, 0);
                        }
    
                        return bitmap;
    
    
    
                    case SKCodecOrigin.RightTop:
                        rotated = new SKBitmap(bitmap.Height, bitmap.Width);
    
                        using (var surface = new SKCanvas(rotated))
                        {
                            surface.Translate(rotated.Width, 0);
                            surface.RotateDegrees(90);
                            surface.DrawBitmap(bitmap, 0, 0);
                        }
    
                        //SKData dataRotated = SKImage.FromBitmap(rotated).Encode(SKEncodedImageFormat.Jpeg, 75);
                        //using (var codec = SKCodec.Create(dataRotated))
                        //{
                        //    SKCodecOrigin orientation2 = codec.Origin;
                        //}
    
                        return rotated;
    
                    case SKCodecOrigin.LeftBottom:
                        rotated = new SKBitmap(bitmap.Height, bitmap.Width);
    
                        using (var surface = new SKCanvas(rotated))
                        {
                            surface.Translate(0, rotated.Height);
                            surface.RotateDegrees(270);
                            surface.DrawBitmap(bitmap, 0, 0);
                        }
    
                        return rotated;
    
                    default:
                        return bitmap;
                }
    
  • mattleibowmattleibow ZAXamarin Team Xamurai

    I think there are a few cases where you are not properly disposing the bitmap, and there are many copies being made. Here I have put together your code, but with more aggressive re-use and disposing:

    private static void ProcessImages(IEnumerable<string> fileEntries, string outputFolder, int quality)
    {
        foreach (string fileName in fileEntries)
        {
            // open the stream, create the codec and get a rotated image
            // here we use a using statement to ensure each obejct gets disposed
            // 
            // technically, we could close the stream and dispose the codec once we have the bitmap
            using (var input = SKFileStream.OpenStream(fileName))
            using (var codec = SKCodec.Create(input))
            using (var bitmap = RotateImage(codec))
            {
                var name = Path.GetFileName(fileName);
                var outPath = Path.Combine(outputFolder, "R_SK_" + name);
    
                // open a stream and save directly without any additional memory
                using (var stream = SKFileWStream.OpenStream(outPath))
                {
                    bitmap.Encode(stream, SKEncodedImageFormat.Jpeg, quality);
                }
            }
        }
    }
    
    public static SKBitmap RotateImage(SKCodec codec)
    {
        // re-use the codec as it is already in memory and the stream is already open.
        var bitmap = SKBitmap.Decode(codec);
    
        // various rotation cases
        switch (codec.Origin)
        {
            // the comments for this case apply to the others
            case SKCodecOrigin.BottomRight:
                {
                    // create a new bitmap to hold the rotated image,
                    // there is nothing we can do about another memory allocation because we have to rotate
                    var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
    
                    // the canvas is fairly light, but make sure we dispose anyway
                    using (var surface = new SKCanvas(rotated))
                    {
                        // rotate and draw on the screen, this just copies the color data - no new allocations
                        surface.RotateDegrees(180, rotated.Width / 2, rotated.Height / 2);
                        surface.DrawBitmap(bitmap, 0, 0);
                    }
    
                    // here we are clever and make sure we dispose the source bitmap
                    bitmap.Dispose();
    
                    // but we reuse the variable so we can have a nice return statment at the end of the method
                    bitmap = rotated;
                }
                break;
    
            case SKCodecOrigin.RightTop:
                {
                    var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
                    using (var surface = new SKCanvas(rotated))
                    {
                        surface.Translate(rotated.Width, 0);
                        surface.RotateDegrees(90);
                        surface.DrawBitmap(bitmap, 0, 0);
                    }
                    bitmap.Dispose();
                    bitmap = rotated;
                }
                break;
    
            case SKCodecOrigin.LeftBottom:
                {
                    var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
                    using (var surface = new SKCanvas(rotated))
                    {
                        surface.Translate(0, rotated.Height);
                        surface.RotateDegrees(270);
                        surface.DrawBitmap(bitmap, 0, 0);
                    }
                    bitmap.Dispose();
                    bitmap = rotated;
                }
                break;
        }
    
        // a nice, single return that will either return the source image, or the rotated image
        return bitmap;
    }
    
Sign In or Register to comment.