Arne
Arne

Reputation: 3074

MonoTouch: Memory leak when drawing PDF on a custom graphics context

I have an app that's basically a fancy PDF reader. I download a PDF from the internet and generate thumbnails for that PDF. However, it seems that when I generate these thumbnails a lot of memory is being allocated (checked using Instruments), sometimes parts of this is collected by the GC but in the end, my app gives up. I've had memory usage of up to 38Mb when generating thumbnails for a single PDF (100x100 thumbs, ~60 pages).

I generate one thumbnail at a time, store it and then repeat the process, so under any circumstance there should only be one thumbnail in memory (while generating them, at least). My code for generating thumbnails looks like this:

public UIImage GetPageThumbnail(int pageNumber, SizeF size)
{
    //If using retina display, make sure to scale-up the thumbnail as well.
    size.Width = size.Width * UIScreen.MainScreen.Scale;
    size.Height = size.Height * UIScreen.MainScreen.Scale;

    UIGraphics.BeginImageContext(size);
    CGContext tempContext = UIGraphics.GetCurrentContext();
    CGPDFPage page = Document.GetPage(pageNumber);

    RectangleF drawArea = new RectangleF(new PointF(0f, 0f), size);
    CGAffineTransform transform = page.GetDrawingTransform( CGPDFBox.Crop, drawArea, 180, true); //fit PDF to context
    transform.xx = -transform.xx;   // }
    transform.x0 = 0;               // }flip horizontally

    //Console.WriteLine("XX: " + transform.xx + ", YX:" + transform.yx + ", XY:" + transform.xy + ", YY:" + transform.yy + ", X0:" + transform.x0 + ", Y0:" + transform.y0);

    tempContext.ConcatCTM(transform);

    tempContext.DrawPDFPage (page);
    UIImage returnImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return returnImage;
}

I've tried explicitly disposing the context and PDF page, but that had no effect (actually it seemed worse, but take that with a pinch of salt).

I've seen some posts about memory leakage with MonoTouch and PDF (basically this post), but that's pretty old. I'm using the newest MonoTouch (5.0.2).

Upvotes: 2

Views: 1668

Answers (1)

Krumelur
Krumelur

Reputation: 33048

Not sure where the problem in your code is, but here's my code for generating thumbs of PDF pages. It is working flawlessly. Maybe it helps you. I think your issue might be what you are doing with the returned image when you're don.

public static UIImageView GetLowResPagePreview (CGPDFPage oPdfPage, RectangleF oTargetRect)
        {
            RectangleF oOriginalPdfPageRect = oPdfPage.GetBoxRect (CGPDFBox.Media);
            RectangleF oPdfPageRect = PdfViewerHelpers.RotateRectangle( oPdfPage.GetBoxRect (CGPDFBox.Media), oPdfPage.RotationAngle);

            // Create a low res image representation of the PDF page to display before the TiledPDFView
            // renders its content.
            int iWidth = Convert.ToInt32 ( oPdfPageRect.Size.Width );
            int iHeight = Convert.ToInt32 ( oPdfPageRect.Size.Height );
            CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB();
            CGBitmapContext oContext = new CGBitmapContext(null, iWidth, iHeight, 8, iWidth * 4, oColorSpace, CGImageAlphaInfo.PremultipliedLast);

            // First fill the background with white.
            oContext.SetFillColor (1.0f, 1.0f, 1.0f, 1.0f);
            oContext.FillRect (oOriginalPdfPageRect);
            // Scale the context so that the PDF page is rendered 
            // at the correct size for the zoom level.
            oContext.ConcatCTM ( oPdfPage.GetDrawingTransform ( CGPDFBox.Media, oPdfPageRect, 0, true ) );
            oContext.DrawPDFPage (oPdfPage);
            CGImage oImage = oContext.ToImage();
            UIImage oBackgroundImage = UIImage.FromImage( oImage );
            oContext.Dispose();
            oImage.Dispose ();
            oColorSpace.Dispose ();

            UIImageView oBackgroundImageView = new UIImageView (oBackgroundImage);
            oBackgroundImageView.Frame = new RectangleF (new PointF (0, 0), oPdfPageRect.Size);
            oBackgroundImageView.ContentMode = UIViewContentMode.ScaleToFill;
            oBackgroundImageView.UserInteractionEnabled = false;
            oBackgroundImageView.AutoresizingMask = UIViewAutoresizing.None;
            return oBackgroundImageView;
        }

Upvotes: 4

Related Questions