Welcome Guest! To enable all features, please Login or Register.

Notification

Icon
Error

Options
View
Last Go to last post Unread Go to first unread post
#1 Posted : Wednesday, June 3, 2015 12:45:28 AM(UTC)
sparcopt

Groups: Registered
Posts: 13


Hello! I'm using Leadtools 19 to create a PDF viewer in C# WPF.

So far I managed to everything I wanted but I'm facing a memory problem. When I open a large PDF file, with about 300 pages, it throws a "You have exceeded the amount of memory allowed for RasterImage allocations" exception message.

I'm using the application provided in the last post of this thread (WPF-v19-ContinuousMultipageScroll.zip): http://support.leadtools...4682/ShowPost.aspx#44682

It seems to me that the problem here is loading all the images to a collection even if most of them are not being viewed by the user.

I'm thinking about loading a small amout of images to a collection and as the user scrolls down, load more images to the collection and remove the old ones to free memory. Is this the best approach? And how I can know the current page number the user is currently viewing?

What are the other approches to solve this problem? There are any options that I can use to load the images from the PDF file with a lower impact on memory?

Thank you for your attention.
 

Try the latest version of LEADTOOLS for free for 60 days by downloading the evaluation: https://www.leadtools.com/downloads

Wanna join the discussion? Login to your LEADTOOLS Support accountor Register a new forum account.

#2 Posted : Wednesday, June 3, 2015 7:11:12 AM(UTC)

JustinF  
Guest

Groups: Guests
Posts: 3,022

Was thanked: 2 time(s) in 2 post(s)

Hi Jose,

As discussed in our chat, the technique needed to avoid memory overflow with this project is called lazy loading:

http://en.wikipedia.org/wiki/Lazy_loading

We implement this in your Document Viewer control for WinForms, but we do not offer this control in WPF.

I'll work on getting some basic lazy loading going in the sample project that you're working with and get back to you as soon as possible. Thanks!
 
#3 Posted : Thursday, June 4, 2015 12:23:23 AM(UTC)
sparcopt

Groups: Registered
Posts: 13


Thank you Justin. I'm looking forward to hear from you.
 
#4 Posted : Friday, June 5, 2015 9:32:12 AM(UTC)

JustinF  
Guest

Groups: Guests
Posts: 3,022

Was thanked: 2 time(s) in 2 post(s)

Jose,

Attached is a project that implements some simple Lazy Loading in WPF.

The application only keeps 3 RasterImages on the heap at a time, and automatically loads more images as they are needed. To do this, I listen for the scroll offset of the ScrollViewer and add/remove pages when the offset reaches either the top page or the bottom page shown. Obviously, these loads are conditional, as we cannot load pages "above" if the top image is page 1, and we cannot load pages "below" if the bottom page is the last page in the document.

There are a number of pitfalls that come along with implementing your own auto page change. The largest of which is typically avoiding infinite page change loops. When new images are added to the loadedPages ObservableCollection, the VerticalScrollOffset of the ScrollViewer changes. This causes your ScrollChanged Event Handler to fire, and you can easily enter an infinite loop if your offset condition(s) is/are the same as the condition(s) that triggered the original page change! So, like I did in the example, you will typically have to REMOVE the offset listener while the pages are being changed, and then re-add the listener once the page changing is complete. I do this in a bit of a hacky way in WPF, because the ScrollViewer ends up queuing UI changes after direct calls to ScrollToVerticalOffset, which delays the firing of the ScrollChanged event.

You'll notice that the scroll bars jump on page change. This is because I am not using any "placeholder" RasterImages in the ObservableCollection. You can opt to do this to avoid the jumpy scroll bar. Also, feel free to mess around with the MAX_PAGES variable to see how many pages can typically be loaded on your machine at your desired DPI. I'm using 96 DPI (fairly low) in this example.

I hope that this helps! Let me know if you have any questions.
File Attachment(s):
WPFContinuousScrollWithLazyLoading.zip (3,180kb) downloaded 50 time(s).
 
#5 Posted : Monday, June 8, 2015 2:46:04 AM(UTC)
sparcopt

Groups: Registered
Posts: 13


Thank you so much for your support! I will take a look at it and report back if needed :)
 
#6 Posted : Thursday, June 11, 2015 4:37:13 AM(UTC)
sparcopt

Groups: Registered
Posts: 13


Can you explain to me your concept of RasterImages "placeholder"?

 
#7 Posted : Monday, June 15, 2015 3:14:06 AM(UTC)

JustinF  
Guest

Groups: Guests
Posts: 3,022

Was thanked: 2 time(s) in 2 post(s)

Jose,

In the context of lazy loading, a placeholder would simply be an empty UI object that, while taking up the same amount of physical space on screen, takes up less memory on the heap. You would then replace the placeholder with your memory-intensive object when it comes into view in an asynchronous fashion. This would allow for a continuous scroll bar position.

 
#8 Posted : Monday, June 15, 2015 4:08:25 AM(UTC)
sparcopt

Groups: Registered
Posts: 13


Yes I tried to do that but with no success. I loaded the 3 first pages and for the rest of the pages I created an empty RasterImage with the same size. But a single empty RasterImage takes about 8MB in memory.

What properties I am missing?
 
#9 Posted : Monday, June 15, 2015 11:53:56 AM(UTC)

Nick  
Nick

Groups: Registered, Tech Support, Administrators
Posts: 161

Was thanked: 9 time(s) in 9 post(s)

If you're creating a RasterImage programatically and appending it to the end of the list as it is being built, try using RasterMemoryFlags.SuperCompressed. For the purpose of this image its content is not relevant, only that it is there. This requires using the CMP codec.
Nick Crook
Developer Support Engineer
LEAD Technologies, Inc.
LEAD Logo
 
#10 Posted : Monday, June 15, 2015 11:45:33 PM(UTC)
sparcopt

Groups: Registered
Posts: 13


Is this the best way to create a RasterImage with the lowest impact on memory?

"Pages" is my ObservableCollection where [0] gives me the width and height of the first page of the pdf. I only load the first 3 from the file, for the rest of the pages, let's say I have 300, I load 297 empty RasterImages.

RasterImage image = new RasterImage
(RasterMemoryFlags.SuperCompressed,
Pages[0].Width, Pages[0].Height,
0, RasterByteOrder.Gray, RasterViewPerspective.BottomLeft,
null,
IntPtr.Zero,
0);
 
#11 Posted : Tuesday, June 16, 2015 12:36:35 PM(UTC)

JustinF  
Guest

Groups: Guests
Posts: 3,022

Was thanked: 2 time(s) in 2 post(s)

A RasterImage is likely not a good choice for a placeholder UI object.

There is no such thing as a "blank" image when it comes to memory. Even if your RasterImage is all white, those white pixels will still need to be held in memory just the same as colored pixels.

Our RasterImageViewer is simply an extension of the base Control object. It would likely be best to come up with your own low-memory white control in the shape of a square that you could insert into a ScrollViewer along with other RasterImageViewers. You may have to blow up the current data binding in the XAML of my sample to do this.

For a simpler implementation, you might want to just look into hosting our WinForms DocumentViewer control in WPF:

https://msdn.microsoft.com/en-us/library/ms751761(v=vs.110).aspx
 
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Powered by YAF.NET | YAF.NET © 2003-2024, Yet Another Forum.NET
This page was generated in 0.429 seconds.