SkiaSharp:多次旋转大图片而不会因为内存管理而导致应用程序崩溃

SkiaSharp: rotate big picture multiple times without crashing the app because of memory management

目标是能够旋转相机应用拍摄的图像。我的代码基于 this rotation and this cropping algorithm. General infos can be found here。仅当使用大图像(例如 2 MB)并且多次旋转(例如 70x)时才会出现此问题。我正在使用具有 3 GB RAM 的 Android 平板电脑。

这是我的代码崩溃的地方:

public void Rotate()
{
    double radians = Math.PI * this.rotationAngle / 180;
    float sine = (float)Math.Abs(Math.Sin(radians));
    float cosine = (float)Math.Abs(Math.Cos(radians));
    int originalWidth = this.originalBitmap.Width;
    int originalHeight = this.originalBitmap.Height;
    int rotatedWidth = (int)(cosine * originalWidth + sine * originalHeight);
    int rotatedHeight = (int)(cosine * originalHeight + sine * originalWidth);

    this.rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);

    using (SKCanvas canvas = new SKCanvas(this.rotatedBitmap))
    {
        canvas.Clear(SKColors.White);
        canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
        canvas.RotateDegrees((float)this.rotationAngle);
        canvas.Translate(-originalWidth / 2, -originalHeight / 2);
        canvas.DrawBitmap(originalBitmap, new SKPoint());
    }

    this.InvalidateSurface();
}

它在 new SKBitmap() 和以下消息的行上崩溃:

06-10 10:26:09.255 I/MonoDroid(19721): UNHANDLED EXCEPTION:
06-10 10:26:09.266 I/MonoDroid(19721): System.Exception: Unable to allocate pixels for the bitmap.
06-10 10:26:09.266 I/MonoDroid(19721):   at SkiaSharp.SKBitmap..ctor (SkiaSharp.SKImageInfo info, System.Int32 rowBytes) [0x00023] in <0665b4d05b9e46c8a33bb731d6f5db83>:0 
06-10 10:26:09.266 I/MonoDroid(19721):   at SkiaSharp.SKBitmap..ctor (SkiaSharp.SKImageInfo info) [0x00009] in <0665b4d05b9e46c8a33bb731d6f5db83>:0 
06-10 10:26:09.267 I/MonoDroid(19721):   at SkiaSharp.SKBitmap..ctor (System.Int32 width, System.Int32 height, SkiaSharp.SKColorType colorType, SkiaSharp.SKAlphaType alphaType) [0x0000b] in <0665b4d05b9e46c8a33bb731d6f5db83>:0 
06-10 10:26:09.267 I/MonoDroid(19721):   at SkiaSharp.SKBitmap..ctor (System.Int32 width, System.Int32 height, System.Boolean isOpaque) [0x00000] in <0665b4d05b9e46c8a33bb731d6f5db83>:0 
06-10 10:26:09.267 I/MonoDroid(19721):   at MyApp.PhotoCropperCanvasView.Rotate() [0x0009e] in C:\Users\some-user\Source\MyApp\PhotoCroppingCanvasView.cs:145 

我尝试过的事情:

  1. 试过Dispose()它:

    this.rotatedBitmap?.Dispose();
    this.rotatedBitmap = new SKBitmap(rotatedWidth, rotatedHeight);
    

    第一次处理时崩溃。

  2. 使用using:

    using (SKBitmap newBitMap = new SKBitmap(rotatedWidth, rotatedHeight))
    {
        using (SKCanvas canvas = new SKCanvas(newBitMap))
        {
            canvas.Clear(SKColors.White);
            canvas.Translate(rotatedWidth / 2, rotatedHeight / 2);
            canvas.RotateDegrees((float)this.picAngle);
            canvas.Translate(-originalWidth / 2, -originalHeight / 2);
            canvas.DrawBitmap(originalBitmap, new SKPoint());
        }
    
        newBitMap.CopyTo(this.rotatedBitmap);
    }
    
    this.InvalidateSurface();
    

    出现相同的行为,我可以将图片旋转几次,但在一定数量(例如 70x)后应用程序崩溃。

我该怎么做才能获得更好的内存管理?我唯一想到的是使用 minituarized/reduced 图像,但这不会阻止崩溃,只会延迟崩溃。

因为我没有收到任何反馈,所以我做了我能做的,这就是缩小图像的使用。有了这个应用程序更稳定,但它并不能 100% 解决问题。

旋转算法保持不变,我发现 调整图像大小效果最好,因为它将我的图像从 ~2 MB 减少到 ~50 KB。在我尝试使用 Android 中的示例实现之前,我没有获得像 FFImageLoading 库那样的效率。使用另一种调整大小算法,我也没有得到调整大小的平滑度。至于尺寸,我使用了当前的页面宽度,纵向和横向位置似乎都可以。