Efficient Drawing / Scrolling in a Canvas

Hello all,

Drawing on a canvas is generally pretty straightforward in Xojo and there are plenty of examples and tutorials on how to do this. However, I’m having a more difficult time finding examples of drawing significantly larger images.

So, my question is pretty straightforward: Does anyone have any tips, suggestions, or recommendations on drawing images that are so large that they require the use of scrollbars?

Is it best to just draw ALL of the elements on the canvas and let the system process what it displays on the screen? Is there an efficient way to draw only the elements visible with the established scrollbar positions, ignoring elements that are “out of bounds”?

And a related question: if I wanted to use a Canvas control to visually represent a multi-page, continuous-scroll document, does anyone have any recommendations for the best way to accomplish this? Creating individual canvas controls for each page at runtime seems silly, but also seems like it may be easier/more efficient than trying to represent multiple pages within a single control.

As always, your two cents are highly appreciated!

Unfortunately Xojo has no built no scroll control per se but you can do it yourself. If you are under OSX you can search the forum for the ScrollView. Otherwise check out the examples provided by Xojo (those ones using containers and a scrollbar).
With that in mind you can create your own custom control.

Use a single Canvas and use the Scrollbars only as a reference to the area you are showing within the larger graphic. Then use the SourceX and SourceY variables of DrawPicture to determine what your origin is. See http://documentation.xojo.com/index.php/Graphics.DrawPicture

So you set your scrollbar bounds to the size of the image, and on scroll repaint the canvas using 0 as your x and y, the canvas size as DestWidth and DestHeight, the scrollbar values as sourceX and sourceY, and canvas size as SourceWidth and SourceHeight.

[quote=253970:@Rob Egal]Unfortunately Xojo has no built no scroll control per se but you can do it yourself. If you are under OSX you can search the forum for the ScrollView. Otherwise check out the examples provided by Xojo (those ones using containers and a scrollbar).
With that in mind you can create your own custom control.[/quote]

A scroll view isn’t really necessary for an image - scrolling sets of controls (which also works) is slightly different (see Examples > Graphics & Multimedia > ObjectsInCanvas > ObjectsInCanvas )

Canvas.Scroll should do just fine here and it clips just fine
There’s examples in Examples > Graphics & Multimedia > CanvasScrolling

It depends on how many elements there are and how long it takes to draw them. I worked on CAD systems in the 80’s where you had thousands of elements and slow processing/drawing. Only drawing what you needed made a huge difference. You’ll have to try it out and see where the tipping point is these days.

The simplest method is to test the corners of the bounding box of each element against the viewable area. If any corner of the element is in the viewable area, or if any corner of the viewable area is in the bounding box of the element, then you need to draw that element.

[quote=253974:@Norman Palardy]A scroll view isn’t really necessary for an image - scrolling sets of controls (which also works) is slightly different (see Examples > Graphics & Multimedia > ObjectsInCanvas > ObjectsInCanvas )

Canvas.Scroll should do just fine here and it clips just fine
There’s examples in Examples > Graphics & Multimedia > CanvasScrolling[/quote]

I was trying to cover both requirements. For images it’s ok to use a canvas only but basically a container is the best idea for both.

[quote=253980:@Tim Hare]It depends on how many elements there are and how long it takes to draw them. I worked on CAD systems in the 80’s where you had thousands of elements and slow processing/drawing. Only drawing what you needed made a huge difference. You’ll have to try it out and see where the tipping point is these days.

The simplest method is to test the corners of the bounding box of each element against the viewable area. If any corner of the element is in the viewable area, or if any corner of the viewable area is in the bounding box of the element, then you need to draw that element.[/quote]

Exactely. if you have a large image you can buffer it and use only the portions via die g.drawpicture.
I kind of think he need some code snipped to get an idea.

I’m getting the impression that there isn’t necessarily one way to do this.

I also get the impression that using multiple Canvas controls is frowned upon. Why? It seems like it would be so much easier. Example: I could have one Canvas that serves as a container; scrollbar controls manipulate this object. Then, each page is represented by a custom “page” class (with the Canvas super) within the container Canvas. Each page canvas within the container canvas is drawn only if visible. It also seems that this approach would make it easier to handle mouse coordinates.

layering controls causes flicker

And, as far as I am aware, there isn’t a way to add controls at runtime, which makes this option finicky anyways.

Thank you all so much for your responses! They have been most helpful.

http://documentation.xojo.com/index.php/New

Control Sets and ContainerControls are 2 ways.

From my own experience, large complex drawings are a PITA. Currently I have a prototype application that draws a map of sorts. The canvas is resized to the full size of the map, which is then inserted into a NSScrollView.

Because it’s so large each element checks to see if actually needs to be drawn before drawing, the trouble is there’s some conflicts here with the ScrollView as the ScrollView will cache segments, but the area() rects in Xojo do not appear to match the area that the scrollview is caching. I’ve tried it also with declares (checking to see if a rect needs updating), and get the same results, great big chunks of nothing as you scroll. The entire canvas must be refreshed, and there’s thousands of objects being drawn, it takes a long time to scroll.

I then switched to using a non-resizing canvas with regular scrollbars, coupled with the areas() array, this improved scrolling speed, but left me feeling like I was building an application from Mac OS 7.x.

I then attempted to overlay a blank canvas and capture the scroll events from a ScrollView, it improved the speed (same as the Mac OS 7 implementation), but came at the cost of other weird issues.

Ideally; what I think we need is a some scrollbars that work like the NSScrollView scrollbars and a static canvas. I don’t seem to be able to reliably re-create them, I can re-invent the wheel or fake them… However that also comes with it’s own problems.

[quote=253992:@Matthew Pool]I’m getting the impression that there isn’t necessarily one way to do this.

I also get the impression that using multiple Canvas controls is frowned upon. Why? It seems like it would be so much easier. Example: I could have one Canvas that serves as a container; scrollbar controls manipulate this object. Then, each page is represented by a custom “page” class (with the Canvas super) within the container Canvas. Each page canvas within the container canvas is drawn only if visible. It also seems that this approach would make it easier to handle mouse coordinates.[/quote]

Yes you can do it on different ways. I have been working with scrolling, canvases and container controls for some time and in my current project I needed to display a couple of containers with about 500x500px which slides horizontally or vertically. (project encrypted-only for demo purposes as am still working on the main project)

https://www.dropbox.com/s/vw2n324nlchuu6g/scrollContainer.xojo_binary_project?dl=0

In other words- create an image in a method, store it in a property, and draw it in the paint event using g.drawpicture? Or did I misunderstand?

Is this a problem on both Mac and Windows? I know Mac tends to be better about preventing flickering… And, I wonder how much flicker would happen if the project consisted of multiple canvas controls that were scrolled, but not layered.

OK. Good point. I must have had a brainfart when writing the earlier post (and also have never had the need to create controls at runtime in Xojo). Still, it doesn’t seem as simple to do this in Xojo as it is in VB.NET, for example.

I’m glad I’m not the only person who thinks so. To me, it seems so silly to have to check EVERY object to determine if it needs to be drawn EVERY time the scroll bar is changed. But it seems like that would be more efficient than redrawing ALL of the elements.

Thanks for the sample project! The first link posted doesn’t work, but the second work. I think I am leaning towards something like this anyways…

[quote=255314:@Matthew Pool]

Thanks for the sample project! The first link posted doesn’t work, but the second work. I think I am leaning towards something like this anyways…[/quote]

I haven’t optimized it for window though. The flicker can be reduced. I may rework it and offer it as seperate class in the addon section.

In my experience of trying to draw fast in a canvas and moving things around you are best drawing in the background in the graphics property of a picture class and then copying part of the picture into your canvas in the paint event. That way you may not need to redraw thousands of objects every paint event but simply copy the picture into the canvas. Also, in your paint event, do some checks to see if you actually need to redraw the canvas, i’ve found that if the canvas has not been hidden behind another window or resized for example, you can skip most of the drawing code in your paint event.

[quote=255314:@Matthew Pool]@Sam Rowlands From my own experience, large complex drawings are a PITA. Currently I have a prototype application that draws a map of sorts. […] Because it’s so large each element checks to see if actually needs to be drawn before drawing […]
I’m glad I’m not the only person who thinks so. To me, it seems so silly to have to check EVERY object to determine if it needs to be drawn EVERY time the scroll bar is changed. But it seems like that would be more efficient than redrawing ALL of the elements.[/quote]
Certainly in our case, the ‘map’ has thousands of objects and can take seconds to re-draw the whole thing; I’ve been looking at other ways of reducing this, but it’s colossal.

I do need to find a way to be even smarter about what’s drawn, for instance if an element is too far to the right, don’t even go into it to draw it’s children.

Thanks for the offer, at the moment, the code is anything but simple, each element has a large amount of properties and can have a large amount of children.

I am going through re-writing a lot and reducing calculations, drawing an element is relatively fast, it’s having to redrawn 1000s of elements where it gets slow.

I would like to use a NSScrollView; but it looks like I really have to use CALayers to get efficiency out of it. With CALayers, comes a whole 'nother level of complication.