News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Graphics, Coordinate systems, etc. questions

Started by Jimg, May 07, 2007, 04:53:49 PM

Previous topic - Next topic

Jimg

Up to now, I have avoided graphics as much as possible, and if I needed to do anything, I copied it from sample code without understanding what was going on.

I now want to create a custom control, which is going to require understanding what is going on graphically.  I have read for several days but the more I read the more conflicting ideas I get so I thought I would ask some simple questions to get this straightened out.

Is it true that the default logical unit for graphics is the pixel?  It looks like I am going to need to use CreatePen, FillRect, Rectangle, MoveToEx, LineTo, DrawText, BitBlit, etc, and they all say they operate in "logical units".  It looks like the default mapping is MM_TEXT   which is one pixel per count, correct?  Is this the default logical unit?

Assuming I have that correct, what is the correct way to convert from the units that windows are specified in?  GetWindowRect, MoveWindow, etc. are specified in screen coordinates.  I want to draw the stuff in the control, so I need to convert to pixels.  It seems I should be able to get a conversion factor once and store it away somewhere for future use rather than calling some bloated api every time.  Is this a bad idea? What is this api that converts from screen coordinates to pixels?

Almost every sample of graphics code I can find repeatedly creates uses and destroys pens, brushes, fonts, etc.  Is there some reason for all this overhead?  Can I create them once at the start of the program and just use them as needed?  Is there some limit as the the number of brushes and pens I can create at one time?  When I close the program, will windows automatically delete them?  Or do they hang around somewhere in memory forever?  All the graphics api say to use DeleteObject when you are done with them, but Windows seems pretty good at cleaning up after a program.  Likewise, is it really necessary to delete all the objects in a device context before deleting the dc?  And doesn't exiting a program automatically delete any created device contexts?

That's enough for now, I just want to get on the right track from the beginning and not find out a week from now that I have to redo everything:)


MichaelW

In answer to the first two questions, this article is old, but I think still valid.

"The default mapping mode is MM_TEXT. One logical unit equals one pixel."
eschew obfuscation

u

The basics:
A bitmap is a 2D array (width * height). Each element of the array is a pixel. Usually it's 24-bit or 32-bit.
A Device context (DC) is just a small structure, containing handles to a bitmap (to draw on), and to draw with: color-info + handles of: a pen, a font, (and some advanced stuff),
Drawing on a DeviceContext actually means "drawing on the DC's bitmap, using the DC's brush/pen/color".
Drawing is done by setting elements in the array. Blitting is done by copying elements from one array (bitmap) to another.

You don't need to always create DCs, bitmaps and stuff always on WM_PAINT - you can have them as globals. Though, it can complicate things, because once you use SelectObject(), you have to manage the returned object too! And properly return it to its original DC.
So, it's a bit of a hassle ^^". But anyway, imagine if all software kept DCs... that have bitmaps in them... each app would use over 4MB just for that!

Maybe a bright day will come when I finally make my sDraw library more useful in any GUI app ^^" (by adding dynamic allocation...)
Please use a smaller graphic in your signature.

Jimg

Thank you Michael and Ultrano.

If I'm understanding this properly, then I can do everything in pixels and ignore the other coordinate systems.  The exception being if I wanted to resize the control, for example, the user sends me a message to make it a certain height and or width, then I would have to do mapwindowpoints or some other conversion to get from what the user would normally put in for a width to convert it to pixels.  What units does a user usually expect such a width to be in?

Ultrano-  Since you brought up the next level of complexity -
(and anyone else that want's to answer)
I plan on doing my drawing on a "hidden" bitmap/DC and blitting to the UserControl the user defined.  Is this the correct way, or should I skip the usercontrol completely and blit directly to the parent DC?  It would make sense to me to go straight to the parent, but I'm sure there is some reason not to.
QuoteYou don't need to always create DCs, bitmaps and stuff always on WM_PAINT - you can have them as globals. Though, it can complicate things, because once you use SelectObject(), you have to manage the returned object too! And properly return it to its original DC.
So you are saying, if the DC is destroyed, the objects selected into it still exist and is creating a "memory leak"?
What about creating all the pens and brushes I need at startup and keeping them to use.  Does this use a lot of memory also?


u

About the resizing - I don't see any problem... almost all functions take and return width/height/position in pixels. Except for some rarely used API. You'll probably never have to use MapWindowPoints.

The PlatformSDK iirc says/hints that a DC won't be released correctly until you return its original hBitmap, hPen, hBrush and hFont to it. Though, I think that because in Win2k/XP and above, reference-counting is used, we can avoid leaks even if we don't return the original handles to a DC. I haven't inspected this. (ProcessExplorer could help you keep track of # of GDI objects allocated )
One thing is for sure: only hBitmaps can use a lot of memory, the other objects are probably around 100 bytes in size.

Yes, it's good to use a backbuffer. Furthermore, it's absolutely necessary if your window is big. Just don't let DefWindowProc process the WM_ERASEBKGND message, as it will introduce flicker even in back-buffered drawing. Back-buffering is necessary, because at any time of your drawing on the window, the OS can decide to send this intermediate visual result onscreen (the sole reason for flicker).
Please use a smaller graphic in your signature.

raymond

Another good reason to use a backbuffer is to be able to easily refresh the window whenever it may be moved, resized, minimized and later restored, etc. In most if not all such cases, Windows sends the WM_PAINT message where all you would have to do is BitBlt the backbuffer instead of redrawing everything.

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

u

raymond, correction: set the hbrBackground of your window's class to null, and watch Windows actually being smart at keeping already-drawn areas :). This won't work if you've set the CS_HREDRAW/CS_VREDRAW or the hbrBackground.
But in cases when your GUI takes ages to compute , it's good, because during resizing/moving you might get the WM_PAINT msg 100 times per second.
Please use a smaller graphic in your signature.

Jimg

OK, I'll keep the backbuffer between WM_PAINT messages.

I'll only need to remake the backbuffer if the size of the actual control is increased in width or height.
   (Remake the backbuffer when I receive the WM_SIZE message and it's an increase)
   (Do not set CS_HREDRAW or CS_VREDRAW)

Set hbrBackground for the class to null.

When I get the WM_ERASEBKGND message, ignore it (return non-zero?)

When I get the WM_PAINT message,
    Do a BeginPaint to get the DC
    blit the backbuffer to the DC.
     


hutch--

Jim,

Make the back buffer the size of the screen 1st time so that you don't have to mess around with destroying it then recreating it over and over again. Just size what you draw on the backbuffer to match the client area of your display window and it works fine.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

u

Quote from: Jimg on May 08, 2007, 01:40:46 PM
When I get the WM_ERASEBKGND message, ignore it (return non-zero?)
Return zero. Actually, either you set the window-class' hbrBackground to null, or you don't let DefWindowProc process WM_ERASEBKGND. Because DefWindowProc returns immediately on that message if hbrBackground is null.

Quote from: Jimg on May 08, 2007, 01:40:46 PM
When I get the WM_PAINT message,
    Do a BeginPaint to get the DC
    blit the backbuffer to the DC.     
Don't forget the EndPaint() too :)


The problem I mentioned with my sDraw lib (that makes GDI drawing much easier and powerful) is that it usually makes a whole 1024x768 bitmap (or of the given screen-size), a DWORD per pixel... and keeps that 4MB buffer all the time. So, it's not rather elegant for small apps. Though, you can create a 400x100 bitmap if you'll be using just that much space - yet, this is not flexible enough.
[same "problem" plagues Hutch's advised method of using GDI]
Please use a smaller graphic in your signature.

Jimg

Ok, thanks.  One thing I'm worried about is if the user has several instances of my control on his app, I would have to keep several 5+ megabyte backbuffers around (1280*1024*4byte/pixel).  Maybe not too bad for 3 or 4, but if more than that we're talking about a lot of memory.  But it would certainly be easier.
Would it be possible to just create a new backbuffer of the appropriate size and blit the current backbuffer to the new one and just recompute the pixels for the added part rather than the whole thing?  Or is there an api I can't find that lets you resize a bitmap?

u

If your Ctl is in static .lib or .dll , you can (will) share the backbuffer for all instances of the control. After all, it's only one thread that draws.
There's no API to resize a bitmap.
Please use a smaller graphic in your signature.

Jimg

If I only use one backbuffer for all instances of the control, then whenever the the users program does something to change the contents of one of the instances of the control, I would have to recreate the entire contents of the backbuffer.  As always, a compromise between speed and memory and overhead :(
Yes, it will be a static lib.  Each instance will have it's own chunk of the heap to keep track of all it's settings and data, including I was thinking, the handle of it's bitmap.

u

Are the graphics in your Ctl very hard to compute/draw? I.e taking 200+ ms. If not, then you needn't keep the precomputed bitmap-data into your backbuffer.
Actually, what is your Ctl about?
Please use a smaller graphic in your signature.

Jimg

Well, ultimately it's going to be graphing control to allow me to drop multiple graphs on a form with many options and types, and update them in realtime. 
For right now, just to learn how to write a control, I'm doing a grid with features I can't find on any existing grids (plus it's nice to be able to add features if and when I want).  I'm trying to gather all the basic information on how to do all this stuff, so even if I don't need to keep the bitmaps now, I thought I would do it just to see how to do the process, and to keep cycles to a minimum.