NO

Author Topic: MapDialogRect stuff!  (Read 12422 times)

JohnF

  • Guest
MapDialogRect stuff!
« on: April 18, 2005, 01:18:41 PM »
The text in the SDK about using MapDialogRect & GetDialogBaseUnits is confusing.

The MapDialogRect function works correctly but the problem occurs if you need to convert pixels back into dialogbase units for say, saving to the registry.

SDK Quote ========================================
To convert these coordinates from dialog box units to pixels, the function retrieves the current horizontal and vertical base units for the dialog box, then applies the following formulas:

Code: [Select]

left   = MulDiv(left,   baseunitX, 4);
right  = MulDiv(right,  baseunitX, 4);
top    = MulDiv(top,    baseunitY, 8);
bottom = MulDiv(bottom, baseunitY, 8);

End Quote ========================================

where baseunitX is the average char width and baseunitY is the char height

however, playing around with this I found that the correct way is to use the following

Code: [Select]

pixels x = MulDiv(diagUnitX, tm.tmAveCharWidth-1, 4);
pixels y = MulDiv(diagUnitY, tm.tmAscent, 8);


You you are interested in proving it use the following code.

Code: [Select]

TEXTMETRIC tm;
HFONT hFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0);
HDC hdc = GetDC(hDlg);
GetTextMetrics(hdc, &tm);
ReleaseDC(hDlg, hdc);
RECT rc;
SetRect(&rc, 10, 40, 500, 340); // set the rect with coords
MapDialogRect(hDlg, &rc);
int left = MulDiv(10, tm.tmAveCharWidth-1, 4);
int top  = MulDiv(40, tm.tmAscent, 8);


You will notice that even when using different size system fonts

Code: [Select]

left == rc.left
top  == rc.top


Using tm.tmAveCharWidth and tm.tmHeight will give the wrong result.

I've tested this using two different PDI settings for the screen font.

And the conversion back to dialogbase units if you need to save these.

Code: [Select]

dialogbaseunitX = MulDiv(pixelX, 4, tm.tmAveCharWidth-1);
dialogbaseunitY = MulDiv(pixelY, 8, tm.tmAscent);
      

John

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
MapDialogRect stuff!
« Reply #1 on: April 18, 2005, 06:28:27 PM »
Interesting. I have to look at the calculations in the IDE.

Pelle
/Pelle

JohnF

  • Guest
MapDialogRect stuff!
« Reply #2 on: April 18, 2005, 06:36:43 PM »
Quote from: "Pelle"
Interesting. I have to look at the calculations in the IDE.

Pelle


I have since discovered that although the above works for 96 & 120 DPI, the two most common settings, it will not work for 150 DPI.

Still confused!

Maybe I have to follow the asm and see what the system (MapDialogRect) is doing.

John

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
MapDialogRect stuff!
« Reply #3 on: April 18, 2005, 09:17:56 PM »
Not sure this helps, but Microsoft says the tmAveCharWidth field isn't accurate enough:

"Average character width and height of a font can be computed as follows:
Code: [Select]

hFontOld = SelectObject(hdc,hFont);
GetTextMetrics(hdc,&tm);
GetTextExtentPoint32(hdc,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",52,&size);


avgWidth = (size.cx/26+1)/2; avgHeight = (WORD)tm.tmHeight;

The tmAveCharWidth field of the TEXTMETRIC structure only approximates the actual average character width (usually it gives the width of the letter "x") and so the true average character width must be calculated to match the value used by the system."

Pelle
/Pelle

JohnF

  • Guest
MapDialogRect stuff!
« Reply #4 on: April 18, 2005, 10:01:54 PM »
Quote from: "Pelle"
Not sure this helps, but Microsoft says the tmAveCharWidth field isn't accurate enough:

"Average character width and height of a font can be computed as follows:
Code: [Select]

hFontOld = SelectObject(hdc,hFont);
GetTextMetrics(hdc,&tm);
GetTextExtentPoint32(hdc,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",52,&size);


avgWidth = (size.cx/26+1)/2; avgHeight = (WORD)tm.tmHeight;

The tmAveCharWidth field of the TEXTMETRIC structure only approximates the actual average character width (usually it gives the width of the letter "x") and so the true average character width must be calculated to match the value used by the system."

Pelle


I'll give that a try, thanks. However, the avgHeight = (WORD)tm.tmHeight is not right, or , doesn't give the same out put as MapDialogRect.


John

JohnF

  • Guest
MapDialogRect stuff!
« Reply #5 on: April 19, 2005, 07:48:34 AM »
Didn't work, in fact was further away from the MapDialogRect result than before.

So, the best way to do this if you need to convert back and forth between dialogbase units and pixels is to decide on a method and stick with it. Provided you stick to the same method for conversions both ways, all will be well.

Here is a func to make the dlg base units.
Code: [Select]

void GetDlgBaseUnits(HWND hDlg) // handle of the dialog
{
TEXTMETRIC tm;
HFONT hFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0);
HDC hdc = GetDC(hDlg);
GetTextMetrics(hdc, &tm);
ReleaseDC(hDlg, hdc);
g_dlgBaseX = tm.tmAveCharWidth-1; // save base units dx & dy
g_dlgBaseY = tm.tmAscent;
}

Use the base units to calculate the pixel coordinates when positioning controls.
Code: [Select]

void MapCtrl(HWND hCrtl, int x, int y, int cx, int cy, BOOL rePaint)
{
x  = MulDiv(x,  g_dlgBaseX, 4);
cx = MulDiv(cx, g_dlgBaseX, 4);
y  = MulDiv(y,  g_dlgBaseY, 8);
cy = MulDiv(cy, g_dlgBaseY, 8);
MoveWindow(hCrtl, x, y, cx, cy, rePaint);
}

To convert pixels back to dlg units
Code: [Select]

unitsX = MulDiv(pixelsX, 4, g_dlgBaseX);
unitsY = MulDiv(pixelsY, 8, g_dlgBaseY);

The above method has been tested on 96, 120 and 150 DPI.

If you only need to position the controls and don't need to convert back to dlg units use the MapDialogRect API instead.

John

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
MapDialogRect stuff!
« Reply #6 on: April 19, 2005, 01:47:35 PM »
I use GetTextExtentPoint32() to calculate the average width in the IDE. In my experience, this will give better results (for some fonts) than tmAveCharWidth.

I use the same metrics to convert both ways, so at least it's consistent. I have no calls to MapDialogRect(), probably because I couldn't figure out the reverse formula. It's probably not exactly as Windows does, but seems to be 'OK' so far...

Pelle
/Pelle

JohnF

  • Guest
MapDialogRect stuff!
« Reply #7 on: April 19, 2005, 01:54:32 PM »
Yes, the trick is not to mix MapDialogRect with anything else.

Provided it works!

John

Mike H.

  • Guest
MapDialogRect stuff!
« Reply #8 on: April 19, 2005, 03:35:28 PM »
Hi John,

Something I recently found out is that when using the text size functions such as GetTextExtentPoint32 - You need to explicitly Select in the font first.
i.e.  hfont = SNDMSG(hDlg,WM_GETFONT,0,0)
       hdc   = GetDC(hDlg)
       oldobj = SelectObject(hdc,hfont)
       GetTextExtentPoint32(.......)
       SelectObject(hdc,oldobj)
       ReleaseDC(hDlg,hdc)

As strange as it seemed, windows uses the default system font for calculations regardless of what font the dialog is set to use unless you Select it into the DC before calling.

Just thought I would point that out, since it had me scratching my head back a little while ago :)

Thanks,
Mike H.

JohnF

  • Guest
MapDialogRect stuff!
« Reply #9 on: April 19, 2005, 05:15:27 PM »
Quote from: "Mike H."

As strange as it seemed, windows uses the default system font for calculations regardless of what font the dialog is set to use unless you Select it into the DC before calling.

Just thought I would point that out, since it had me scratching my head back a little while ago :)

Mike H.


Yes you are right, peculiar that.

That has helped to clear up some confusion.

Thanks.

John