简体   繁体   English

在将PDF页面呈现为CGBitmapContext时,如何减少内存使用量?

[英]How can I reduce memory usage when rendering a PDF page into a CGBitmapContext?

I'm using the code below to render a preview of a PDF page. 我正在使用下面的代码来呈现PDF页面的预览。 However it is using loads of memory (2-3MB per page). 但它使用大量内存(每页2-3MB)。

In the device logs I see: 在设备日志中,我看到:

<Error>: CGBitmapContextInfoCreate: unable to allocate 2851360 bytes for bitmap data

I really don't need the bitmap to be rendered in 8bits per color channel. 我真的不需要每个颜色通道以8位渲染位图。 How can I change the code to have it rendered in grayscale or less bits per channel? 如何更改代码以使其以灰度或每通道更少的位数呈现?

I would also be fine with a solution where the bitmap is rendered in a maximum resolution of x/y and then the resulting image is zoomed to the requested size. 我也可以使用一种解决方案,其中位图以x / y的最大分辨率渲染,然后将生成的图像缩放到所需的大小。 The PDF will be rendered in detail afterwards by a CATiledLayer anyway. 之后, CATiledLayer将详细呈现PDF。

Also according to Apple's documentation, CGBitmapContextCreate() returns NIL if the context cannot be created (because of memory). 另外根据Apple的文档,如果无法创建上下文(由于内存), CGBitmapContextCreate()将返回NIL。 But in MonoTouch there is only the constructor to create a context, hence I'm unable to check if creation failed or not. 但是在MonoTouch中只有构造函数来创建上下文,因此我无法检查创建是否失败。 If I was able to, I could just skip the pretender image. 如果我能够,我可以跳过伪装图像。

UIImage oBackgroundImage= null;
using(CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB())
// This is the line that is causing the issue.
using(CGBitmapContext oContext = new CGBitmapContext(null, iWidth, iHeight, 8, iWidth * 4, oColorSpace, CGImageAlphaInfo.PremultipliedFirst))
{
    // Fill background white.
    oContext.SetFillColor(1f, 1f, 1f, 1f);
    oContext.FillRect(oTargetRect);

    // Calculate the rectangle to fit the page into. 
    RectangleF oCaptureRect = new RectangleF(0, 0, oTargetRect.Size.Width / fScaleToApply, oTargetRect.Size.Height / fScaleToApply);
    // GetDrawingTransform() doesn't scale up, that's why why let it calculate the transformation for a smaller area
    // if the current page is smaller than the area we have available (fScaleToApply > 1). Afterwards we scale up again.
    CGAffineTransform oDrawingTransform = oPdfPage.GetDrawingTransform(CGPDFBox.Media, oCaptureRect, 0, true);

    // Now scale context up to final size.
    oContext.ScaleCTM(fScaleToApply, fScaleToApply);
    // Concat the PDF transformation.
    oContext.ConcatCTM(oDrawingTransform);
    // Draw the page.
    oContext.InterpolationQuality = CGInterpolationQuality.Medium;
    oContext.SetRenderingIntent (CGColorRenderingIntent.Default);
    oContext.DrawPDFPage(oPdfPage);

    // Capture an image.
    using(CGImage oImage = oContext.ToImage())
    {
        oBackgroundImage = UIImage.FromImage( oImage );
    }
}

I really don't need the bitmap to be rendered in 8bits per color channel. 我真的不需要每个颜色通道以8位渲染位图。

... ...

using(CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB())

Have you tried to provide a different color space ? 您是否尝试过提供不同的色彩空间?

where the bitmap is rendered in a maximum resolution of x/y 其中位图的最大分辨率为x / y

... ...

using(CGBitmapContext oContext = new CGBitmapContext(null, iWidth, iHeight, 8, iWidth * 4, oColorSpace, CGImageAlphaInfo.PremultipliedFirst))

You can control the bitmap size too and other parameters that directly affect how much memory is required by the bitmap. 您也可以控制位图大小以及直接影响位图所需内存量的其他参数。

Also according to Apple's documentation, CGBitmapContextCreate() returns NIL if the context cannot be created (because of memory). 另外根据Apple的文档,如果无法创建上下文(由于内存),CGBitmapContextCreate()将返回NIL。

If an invalid object (like null ) is returned then the C# instance will have an Handle equals to IntPtr.Zero . 如果返回了无效对象(如null ),则C#实例将具有等于IntPtr.ZeroHandle This is true for any ObjC object, since init can return nil and a .NET constructor cannot return null . 对于任何ObjC对象都是如此,因为init可以返回nil并且.NET构造函数不能返回null

Also according to Apple's documentation, CGBitmapContextCreate() returns NIL if the context cannot be created (because of memory). 另外根据Apple的文档,如果无法创建上下文(由于内存),CGBitmapContextCreate()将返回NIL。 But in MonoTouch there is only the constructor to create a context, hence I'm unable to check if creation failed or not. 但是在MonoTouch中只有构造函数来创建上下文,因此我无法检查创建是否失败。 If I was able to, I could just skip the pretender image. 如果我能够,我可以跳过伪装图像。

This is actually easy: 这实际上很简单:

CGBitmapContext context;
try {
    context = new CGBitmapContext (...);
} catch (Exception ex) {
    context = null;
}

if (context != null) {
    using (context) {
        ...
    }
}

or you could also just surround the entire using clause in an exception handler: 或者您也可以在异常处理程序中包围整个using子句:

try {
    using (var context = new CGBitmapContext (...)) {
        ...
    }
} catch {
    // we failed
    oBackgroundImage = null;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM