2012-05-15 2 views
3

Я использую следующий код для рендеринга страницы PDF. Однако он использует массу памяти (2-3 МБ на страницу).Как уменьшить использование памяти при рендеринге страницы PDF в CGBitmapContext?

В журналах устройства я вижу:

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

Я действительно не нужно растровое быть оказаны в 8bits на каждый цветовой канал. Как я могу изменить код, чтобы он отображался в оттенках серого или меньше битов на канал?

Я также был бы в порядке с решением, в котором растровое изображение отображается с максимальным разрешением 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

Я действительно не нужно растровое изображение, чтобы быть оказаны в 8bits на каждый цветовой канал.

...

using(CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB()) 

Вы пытались обеспечить различное цветовое пространство?

где битовая карта отображается в максимальном разрешении х/у

...

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,8 МБ ОЗУ. На 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 и context.Handle! = IntPtr.Zero) {' будет охватывать оба случая. – poupou

+1

@ Krumelur: Sebastien подходит для общего случая, но в этом конкретном случае (CGBitmapContext/CGContext) дескриптор проверяется при построении (вы используете это исключение: https://github.com/mono/maccore/ блоб/ведущий/SRC/CoreGraphics/CGContext.cs # L136) –

Смежные вопросы