Confused about Pictures, Graphics & Canvas

We wish to manage Control Set of Canvas controls each with a Backdrop as (a scaled) Picture and some Graphics as Graphics. Can somebody point us to the most complete and readable documentation in this regard. Some of the issues encountered include …

  1. Why is Canvas.Backdrop NOT initialised with the Canvas ? How is this done ? It is forever throwing NilObjectExceptions.
  2. Does the Canvas.Backdrop also have its “own” Graphics layer (Canvas.Backdrop.Graphics.Draw…) as well as the Canvas itself (g.Draw…) ?
  3. There seems to be numerous ways to achieve the same Picture and Graphics placement but what is considered “best practice” ?
  4. Canvas.Backdrop seems to automatically initialise if one points it to a Picture object early in the programme, but returns nothing if used with DragItem ?

The embedded help system is succinct but not that detailed while the books we have been able to find are introductory or dated. Any and all thoughts appreciated … Ian

Best practice is to use the Paint event handler for all your drawing. Backdrop is limited in its usefulness.

The backdrop property is only for very simple image display – you can’t scale it or control when it draws or anything. You’re much better off doing as Paul says and draw the picture manually in the paint event.

What I often do is create my own canvas subclass with it’s own internal picture property which I can set externally via an exposed method. That method then forces the redraw and the paint event can draw the picture scaled or make other dynamic modifications as required. The advantage of this approach is that you get all the benefits of backdrop but with extra capabilities, and you since you’re drawing the picture yourself, you can ensure it’s not nil and prevent nilObject errors.

Oh great. Thanks for the ideas. I was perhaps placing too much faith in the functionality of BackDrop. Let me go work this some more.

Am I right in saying that a Canvas has a dedicated graphics layer/class as well as a BackDrop layer/class and that all Pictures have a related Graphics layer/class ?

Ian,

you can create an offscreen picture (with New Picture (x,y), draw there what you want to get in the Canvas and set it to Canvas’ Backdrop.

Am I right in saying that a Canvas has a dedicated graphics layer/class as well as a BackDrop layer/class and that all Pictures have a related Graphics layer/class ?

Yes, more or less.

You can draw to Canvas Graphics and have an image into the same Canvas Backdrop.

That is what you can; now yor usage will depend on what you what to do.

Using:

Canvas1.BackDrop = MyNice Pict

and

Canvas1.Graphics.DrawString "Some Text",10,10

Allows you to have a persistant image 'in the background" and be able to add other things (text or drawings) above it.

I’m still missing something here!

When executing me.Backdrop.Graphics.DrawPicture in a Canvas … as a stand alone control, in a Control Array or as a Canvas in a Container, the Backdrop is not initialised, even though I have valid pointer to the parent Canvas or Container objects. I’d rather load the Backdrop in code as opposed to the Inspector but it remains elusive. I’m sure it’s possible, but what’s the secret code … ?

To initialize the Backdrop, do

Me.Backdrop = MyPicture

But really, don’t do that. Instead draw MyPicture in the Paint event handler:

g.DrawPicture(MyPicture, 0, 0)

And follow that with your other drawing code.

A Canvas does not store what is drawn on it. I think that’s where you’re getting confused. It sounds like you expect the Backdrop to contain the contents of the canvas. It doesn’t. It’s a quick and dirty way of getting a fixed image to display on the canvas. Consider it a one-way street.

If you want the “contents” of the canvas, there are a couple of ways to approach it.

  1. Draw everything to a buffer Picture object and then draw the picture to the canvas. best
  2. Use DrawInto to cause the Canvas Paint event to fire. (may or may not actually work, you may have to DrawInto the entire window and extract the area that the canvas occupies)
  3. Take a screen shot and extract the area that the canvas occupies.

OK … I now grasp the tool much better. For others who may read this conversation the key points for me were as follows …

  1. Do all Graphics in the Paint event based upon Properties (flags, values and Pictures) set in all the other related events for Mouse, Drag etc.
  2. Backdrop is not an enduring property of a Canvas and best not used.
  3. Backdrop is best initialised by assigning it a pointer to an existing Picture object of the right size. Me.Backdrop = aPicture
  4. All Picture objects, including Backdrop, have Graphics layer/class that support Methods for scaling, cropping etc. aNewPicture.Graphics.DrawPicture anOriginalPicture, 0, 0, aNewPicture.Width, aNewPicture.Height, 0, 0, anOriginalPicture.Width, anOriginalPicture.Height

Feel free to correct my homework lest I’m still adrift but all these principles are now working just fine for me.

My thanks to … Tim, Paul, Emile & Marc

Something I always try and do is reduce as much as possible the amount of work which is being carried out in the paint event. It is very easy to quickly start doing lots and lots of tasks in the paint event and really putting your CPU to work. Try profiling your code when it is running and have a look at what is being excessively carried out.

One thing I noted in one of my apps was that I was recalculating a gradient on every paint event and then drawing it. I then found that I was better of calculating the gradient once and storing it in a variable of type picture, then in your paint event just copy the picture into your canvas… This massively reduced overhead.

Also, check for your canvas resizing. You will probably find there are many things you only need to redraw if the canvas is resized. At the end of your paint event store g.width and g.height in static variables. At the start of the paint event check if the width or height have changed and only redraw things you need to.

To me the key was using the code profiler to find bulky code which could easily be reduced down.

Bare in mind, what may look fine on your fast all singing all dancing machine may be slow, flickery and not pretty on an older machine if you have not optimised your painting.

Backdrop IS an enduring property once assigned a picture. (And best not used.)

But things you draw during the Paint event do not become part of the image which is the backdrop.

If you draw everything in Paint, what you draw is not enduring: covering up with another window would ‘erase’ it, but moving the window causes a Paint event and your code to draw the picture/text is called again.

If you need to save the combination of the picture you paint plus any text that you add, the best way is to create picture as a property of the window.
Draw your picture to its graphics. Add your text to its graphics.
And in the paint event of the canvas, paint the picture to the canvas.
You can save ‘the picture’ at any time.

[quote=59711:@Ian Ramsay]Backdrop is not an enduring property of a Canvas and best not used.
Backdrop is best initialised by assigning it a pointer to an existing Picture object of the right size. Me.Backdrop = aPicture[/quote]

The “best not used” may not necessarily be true.

If you do not need to draw often in Paint, the backdrop property is a perfectly valid way of displaying a picture.

Furthermore, since backdrop IS PERMANENT (enduring), unlike the g object in paint, it does not need extra code to display.

Finally, if you draw in Paint, everything disappears unless you keep drawing it. If you create a picture property and assign it as backdrop, you can use its graphics property to draw, and the result will remain even if the code is not repeated in Paint.

Contrary to misguided popular belief, drawing to Picture.Graphics is not forbidden because it takes place out of the Paint event, and is actually a really nice way to draw complex things without wasting time redrawing it time and again in Paint. A good example of why NOT using paint would be very complex calculus to draw the result of equations. If the calculus take, say, a dozen seconds, it would be stupid to do it in every Paint occurrence.

What IS forbidden is Canvas.Graphics, but that is a practice that belongs in the dusty past of RB.

Couldn’t agree more…

I only ever tend to use the backdrop property for prototypes. If you want to quickly mock-up something then the backdrop property makes a perfectly valid choice.

Sometimes the sloppy way of doing things can be good for testing.

I agree with Michel also.

Nothing wrong with backdrop at all. If I just want to display a graphic on a window etc and don’t need to scale it then I just set it in the backdrop property. Job done.

Indeed. And even if you need to scale it, it makes much more sense to do that once upon the opening of the canvas, rather than to repaint it over and over.

Hello,
till now, developing for Mac only, I thought I did not have any problem assigning an imported picture to a canvas.backdrop.
After creating in the Finder myPct (50x200) and myPctx2 (100x400), I would drop them into the IDE, and in the open event of the canvas I would write: canvas1.backdrop = myPct. Then, running the app, my Retina MBP would display the expected image with dimension 50x200.
Today I made a windows-build, and when I tested the app in a non-retina-aware PC (Windows 10), I found that the canvas shows the image at 100x400. In order to get the expected 50x200 picture, I had to remove the 100x400 one.

Therefore my question: in cases as simple and straight as this one, should one provide one picture only (50x200), or both the 50x200 and the 100x400?
If the answer is that both pictures should be used, what could be the problem? The Info in Preview app shows:
Depth 8
DPI Height 144
DPI Width 144
Has alpha 1
and the height/width of the two pictures as above.

Using Xojo 2016v1.1 and OSX 10.11.6

You should make sure the 50x200 pic is truly 72 dpi.

Hi Carlo,

Image 1: 50x200: 72dpi
Image 2: 50x200@2x: 144dpi

No 100x400 (even if you feel this exists). If that image is displayed larger than 50x200, then the application display is set to “Show all points” (or so).

People usually are tricked because of the application (whatever it is) display the 144dpi images “larger” than the 72dpi. Fortunately, they are starting to learn that an image at 144dpi is <> than its “twin” at 72dpi. :wink:

So, after duplicating the 100x400 144 dpi pict, I changed its resolution to 72 and its dimensions became 50x200. Now it shows all right in the PC windows.
Am I right in keeping the original 100x400 pic at 144 dpi? Since I do not have a retina-aware PC Windows, I cannot check.
Thank you, Michele and Emile, for your help.