2012-05-15 13 views
3

我正在使用下面的代码来呈现PDF页面的预览。但是它使用大量的内存(每页2-3MB)。如何在将PDF页面呈现到CGBitmapContext时减少内存使用量?

在设备日志,我看到:

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

我真的不需要位图中每个颜色通道8位来呈现。如何更改代码以使其呈现灰色或每通道较少的位数?

我也会很好的解决方案,位图以最大分辨率x/y呈现,然后生成的图像放大到所需的大小。无论如何,PDF将在后面以CATiledLayer进行详细描述。

同样根据Apple的文档,如果无法创建上下文(由于内存),CGBitmapContextCreate()返回NIL。但是在MonoTouch中只有构造函数来创建上下文,因此我无法检查创建是否失败。 如果我能够,我可以跳过伪装者的形象。

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); 
    } 
} 

回答

2

我真的不需要位图中每个颜色通道8位来呈现。

...

using(CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB()) 

你试图提供不同的色彩空间?

,其中位图中的x/y

的最大分辨率渲染...

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

您可以控制位图的大小也和其他参数的多少直接影响内存是位图所需的。

另外根据Apple的文档,如果上下文无法创建(因为内存),CGBitmapContextCreate()返回NIL。

如果一个无效的对象则返回(如null)的C#实例将有一个Handle等于IntPtr.Zero。对于任何ObjC对象都是如此,因为init可以返回nil,并且.NET构造函数不能返回null

+0

我做了一个有趣的发现:这个问题是在iPad上3只!在高分辨率的iPad 3 b/c上,位图使用2.8MB的RAM。在iPad 1和2上,相同的位图使用700k。我认为iOS中存在一个问题 - 它应该真的警告我,但它会在没有任何警告的情况下死亡。我现在将最大位图分辨率限制为700k。 – Krumelur

2

另外根据Apple的文档,如果上下文无法创建(因为内存),CGBitmapContextCreate()返回NIL。但是在MonoTouch中只有构造函数来创建上下文,因此我无法检查创建是否失败。如果我能够,我可以跳过伪装者的形象。

这其实很简单:

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

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

,或者您也可以只围绕所使用的子句整个在异常处理:

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

然后Sebastien的解决方案是谁检查context.Handle == IntPtr.Zero无法工作,因为“上下文”不会是一个有效的参考。你们两个是谁? :-) – Krumelur

+0

@Krumelur:两者。它取决于类库是否执行检查(非常罕见),并对使用null'Handle'创建的实例执行一些其他调用(更常见)。如果它被使用(例如内部),那么你*可能*得到一个异常(在你有机会检查'Handle'之前),但你不能依赖获得一个异常*只捕获无效实例。 OTOH如果.ctor抛出你将无法要求它的处理;-)。改变上面的if(context!= null){'for'if(context!= null and context.Handle!= IntPtr.Zero){'将覆盖这两种情况。 – poupou

+1

@Krumelur:Sebastien适用于一般情况,但在这种特殊情况下(CGBitmapContext/CGContext),在构造时检查句柄(您遇到以下异常:https://github.com/mono/maccore/斑点/主/ SRC/CoreGraphics中/ CGContext.cs#L136) –