Can you measure the width of a string more exactly in WIN32 than using the GetTextMetrics function and using tmAveCharWidth*strSize?
Try using GetTextExtentPoint32. That uses the current font for the given device context to measure the width and height of the rendered string in logical units. For the default mapping mode, MM_TEXT, 1 logical unit is 1 pixel.
However, if you've changed the mapping mode for the current device context, a logical unit may not be the same as a pixel. You can read about the different mapping modes on MSDN. With the mapping mode, you can convert the dimensions returned to you by GetTextExtentPoint32 to pixels.
I don't know for certain, but it seems that:
HDC hDC = GetDC(NULL);
RECT r = { 0, 0, 0, 0 };
char str[] = "Whatever";
DrawText(hDC, str, strlen(str), &r, DT_CALCRECT);
might work.
Graphics::MeasureString ?
VOID Example_MeasureString(HDC hdc)
{
Graphics graphics(hdc);
// Set up the string.
WCHAR string[] = L"Measure Text";
Font font(L"Arial", 16);
RectF layoutRect(0, 0, 100, 50);
RectF boundRect;
// Measure the string.
graphics.MeasureString(string, 12, &font, layoutRect, &boundRect);
// Draw a rectangle that represents the size of the string.
graphics.DrawRectangle(&Pen(Color(255, 0, 0, 0)), boundRect);
}
Depending on how you are using this, you can use DrawText with DT_CALCRECT specified and it will (its always done it fairly accurately for me) calculate the size of the required rectangle based on the text/font/etc.
For Builder C++ first make new TLabel dynamicly and then change font attributes.Set your TLabel as autosize.Then you can get you TLabel width witch represents your string width in pixels.
int WidthPixels (String font, int size, String text)
{
TLabel* label = new TLabel(Form1); // dynamic TLabel
label->AutoSize = true;
label->Font->Name = font; // your font
label->Font->Size = size; // your font size
label->Caption = text; // your string
return label->Width;
}
int width = WidthPixels("Times New Roman", 19 , "Hey");
Related
I'm currently using 5MP Camera, so I convert BYTE* to GDI+ Bitmap object and uses Graphics object to draw on picture control (all GDI+ objects)
and I want to draw a string on it and when I do so, resolution (quality or whatsoever) gets strange. here're the images.
this is the original image
this is the image with the text on it
And here's my code. it uses MFC's WM_MOUSEMOVE. and when mouse pointer gets on CRect(dispRC[array]), it renders string "aa" on the Bitmap object.
and when I do so, quality of image gets lower or I don't exactly know it changes the IMAGE. (You might not notice because those are captured images, but latter image's quality gets lower.)
void CSmall_StudioDlg::OnMouseMove(UINT nFlags, CPoint point)
{
CPoint insidePoint;
// MAXCAM is the number of bitmap objects.
for (int i = 0; i < MAXCAM; i++)
{
// m_pBitmap[MAXCAM] is array of Bitmap* which contains address of Gdiplus::Bitmap objects.
if (m_pBitmap[i] != NULL)
{
// m_rcDisp[MAXCAM] are CRect objects which has information of picture control.
// i.e. GetDlgItem(IDC_BIN_DISP)->GetWindowRect(m_rcDisp[BINARY_VID]);
if (point.x > m_rcDisp[i].TopLeft().x && point.y > m_rcDisp[i].TopLeft().y)
{
if (point.x < m_rcDisp[i].BottomRight().x && point.y < m_rcDisp[i].BottomRight().y)
{
StringFormat SF;
insidePoint.x = point.x - m_rcDisp[i].TopLeft().x;
insidePoint.y = point.y - m_rcDisp[i].TopLeft().y;
Graphics textG(m_pBitmap[i]);
textG.SetTextRenderingHint(TextRenderingHintSingleBitPerPixel);
Gdiplus::Font F(L"Palatino Linotype Bold", 10, FontStyleBold, UnitPixel);
RectF R(insidePoint.x, insidePoint.y, 20, 100);
SF.SetAlignment(StringAlignmentCenter);
SF.SetLineAlignment(StringAlignmentCenter);
SolidBrush B(Color(0, 0, 0));
textG.DrawString(_T("aa"), -1, &F, R, &SF, &B);
// m_pGraphics[MAXCAM] is made like this
// i.e.
// static CClientDC roiDc(GetDlgItem(IDC_ROI_DISP));
// m_hDC[ROI_VID] = roiDc.GetSafeHdc();
// m_pGraphics[ROI_VID] = Graphics::FromHDC(m_hDC[ROI_VID]);
m_pGraphics[i]->DrawImage(m_pBitmap[i], 0, 0, m_vidwidth[i], m_vidheight[i]);
}
}
}
}
CDialogEx::OnMouseMove(nFlags, point);
}
Hope I get a helpful answer.
Thanks!
I had a similar problem and solved it by creating the GDI+ font from a Windows font like this:
Gdiplus::Font font(hDc, hFont);
where hDc is a DC handle and hFont is a font handle.
Can you try if this helps?
I'm working on a MFC project with some GDI drawings.
I use DC.DrawText to draw a vertical text into a DC using a LOGFONT with lfEscapement = 900.
The text is output when i use DT_NOCLIP in the desired vertical formatting.
However to center this text i used a call to DC.DrawText with the DT_CALCRECT argument.
I recognized that, despite the text is indeed drawn vertically, the CRect has a larger width
than height.
My intuition says me that a vertical drawn text should have a larger height than width.
I did not include the calculation for centering the text. The question is just about what i can rely upon when i implement that vertical centering.
Does DC.DrawText with DT_CALCRECT ignore escapement?
void CMFCFontTestDlg::OnPaint()
{
CPaintDC dc(this); // Gerätekontext zum Zeichnen
if (IsIconic())
{
...
}
else
{
CDialogEx::OnPaint();
CRect clTextRect;
CFont myFont;
myFont.CreateFont(12, 0, 900, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("Tahoma"));
CFont* oldFont = dc.SelectObject(&myFont);
dc.DrawText(_T("000000"), clTextRect, DT_CALCRECT);
clTextRect.MoveToXY(100, 100);
dc.DrawText(_T("000000"), clTextRect, DT_NOCLIP);
dc.SelectObject(oldFont);
}
}
I found out about it in the remarks to the DrawTextEx function
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawtextexa.
However, please note that neither the documentation of DrawText does say anything about this nor the documentation of the DT_CALCRECT flag.
This is likely to be overseen if one uses DrawText and not DrawTextEx.
I informed MS about this via "Is this page helpful?" feedback possibility.
Remarks
The DrawTextEx function supports only fonts whose escapement and
orientation are both zero.
The text alignment mode for the device context must include the
TA_LEFT, TA_TOP, and TA_NOUPDATECP flags.
Considering this the solution is to use some trigonometric calculation starting from the size determined for escapement = 0 and then calculate the topleft and bottomright points of the rotated rect.
Windows API resources can cause memory leaks if not taken care of properly. Whether that's the case or not in this issue, I'm assuming it's related. Although I show how I pinned down to where the issue is coming from, I haven't been able to solve it.
I have two types of static class controls using the Win32 API, which is abstracted in my classes:
Label
LinkLabel
The issue: Whenever I add these two controls, Visual Studio 2017's Diagnostic Tools shows Process Memory (MB) increasing from 3MB to 11MB when I have either both setFont() or setHoverColor() lines enabled, and eventually everything in my GUI space disappears -- gone from existence, like some well-known bookstores.
This code is fine (3MB stays the same constant rate in Process Memory):
// Linked Label
myLinkLabel.init("http://www.google.com", 50, 450);
myLinkLabel.setColor(0, 0, 255);
myLinkLabel.onClick(lbl_Click);
myLinkLabel.setFont("Arial", 40, true);
//lbl.setHoverColor(255, 0, 0);
// label
myLabel.init("A regular static label", 0, 0);
myLabel.setColor(0, 255, 0);
myLabel.setFont("Arial", 40);
//myLabel.setHoverColor(255, 0, 0);
This next code uncomments the last line. After hovering over myLabel, and the red highlight color appears, Process Memory's 3MB increases to 7MB+. It sits for a bit, then goes up to 9MB+. So, something is wrong in it.
// Linked Label
myLinkLabel.init("http://www.google.com", 50, 450);
myLinkLabel.setColor(0, 0, 255);
myLinkLabel.onClick(lbl_Click);
myLinkLabel.setFont("Arial", 40, true);
//lbl.setHoverColor(255, 0, 0);
// label
myLabel.init("A regular static label", 0, 0);
myLabel.setColor(0, 255, 0);
myLabel.setFont("Arial", 48);
myLabel.setHoverColor(255, 0, 0);
So, let's dig into my setHoverColor():
void Label::setHoverColor(const BYTE red, const BYTE blue, const BYTE green)
{
m_hoverColorEnabled = true;
m_hoverColor = RGB(red, green, blue);
}
Okay, nothing too amazing in the code above. This tells me to look in WndProc.
The events this static control uses is WM_SETCURSOR.
case WM_SETCURSOR:
{
HWND m_handle = (HWND)wParam;
// Label
for (int i = 0; i < frm.getLabelControlCount(); i++)
{
if (frm.getLabelControl(i).getHandle() == m_handle)
{
if (frm.getLinkLabelControl(i).isLink())
{
// Set hover color to link
if (frm.getLabelControl(i).isHoverColorEnabled())
frm.getLabelControl(i).setColor(frm.getLabelControl(i).getHoverColor());
// Update cursor to hand
SetClassLongPtr(frm.getLabelControl(i).getHandle(), GCLP_HCURSOR, (LONG_PTR)frm.getLabelControl(i).getHoverCursor());
}
}
else
{
// Set link to blue and use default arrow
if (frm.getLabelControl(i).isHoverColorEnabled())
frm.getLabelControl(i).setColor(0, 0, 255);
SetClassLongPtr(frm.getLabelControl(i).getHandle(), GCLP_HCURSOR,
(LONG_PTR)LoadCursor(NULL, IDC_ARROW));
}
}
When commenting this section of code, Process Memory stays constant at 3MB. When uncommenting this section, Process Memory increases. So, this is the main code that's causing the problem apparently.
This section of code is basically updating the label's text color based on its current mouse hovering state. It's blue when not hovered over, and it's red when hovered over.
setColor() is the following code:
void Label::setColor(const COLORREF color)
{
m_foreColor = color;
setFont(m_fontName, m_fontSize, m_bold, m_italic, m_underlined);
}
Which is also calling setFont() to update it:
bool Label::setFont(const std::string &fontName, const int size, const bool bold,
const bool italic, const bool underlined)
{
DWORD dwItalic;
DWORD dwBold;
DWORD dwUnderlined;
SIZE linkSize;
dwItalic = (italic) ? TRUE : FALSE;
dwBold = (bold) ? FW_BOLD : FW_DONTCARE;
dwUnderlined = (underlined) ? TRUE : FALSE;
m_font = CreateFont(size, 0, 0, 0, dwBold, dwItalic, dwUnderlined, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, fontName.c_str());
SendMessage(m_handle, WM_SETFONT, WPARAM(m_font), TRUE);
// Calculate the correct width and height size
HDC hDC = GetDC(m_handle);
SelectFont(hDC, m_font);
GetTextExtentPoint32(hDC, m_text.c_str(), (int) m_text.length(), &linkSize);
setSize(linkSize.cx, size);
// Store font information
m_fontName = fontName;
m_fontSize = size;
m_bold = bold;
m_underlined = underlined;
m_italic = italic;
return true;
}
My guess is this is a lot of updating for creating a font and re-creating it based on every hover. My reasoning was it wouldn't update the font color unless setting the font again. Although I see room for this in the near future, am I forgetting to delete a resource or something here? Any thoughts are welcomed. Pretty sure this will solve LinkLabel as well.
Your basic problem is that you keep generating new fonts and never releasing the old ones.
Each time setfont is called you allocate and select a new font. But when you select the NEW font into the HDC you never clean up the old font.
SelectFont returns the previously selected font which you need (unless it is a stock font) to do a DeleteFont on.
Additionally you have a bigger resource leak on the GetDC call - the MS documentation for getDC suggests that you use a releaseDC when you have completed the usage.
As far as I understand it is NOT required to reset the font just to reset the color.
I am trying to draw two pieces of text one by one in MFC as they would be part of the same text. Right now I am drawing them as they are just one string:
CString text1 = "A text";
CString text2 = "A second text";
CString textToDraw = text1 + text2;
CDC* dc = GetDC(); //assume that this is initialized elsewhere
dc->TextOut(0, 0, textToDraw);
It is simple to draw the both texts as one because I only need to find the position where they should be started to be draw. The problem I am facing is how to compute the new X coordinate at which the second text should be draw (considering that the texts can be chosen at run-time so they do not have a known length):
dc->TextOut(0, 0, text1);
int X;
//how should I compute X...?
dc->TextOut(X, 0, text2);
I appreciate any help received!
You can use DrawText() with DT_CALCRECT flag to calculate the width and height the text would occupy without actually drawing the text. The following may be the answer to your question.
dc->TextOut(100, 100, text1);
RECT rect = { 0, 0, 0, 0 };
dc->DrawText(text1, &rect, DT_CALCRECT);
dc->TextOut(100 + rect.right, 100, text2);
The code below calculates the size of a the rect holding the text to a checkbox. Code works fine when i'm using a stationary computer with monitors with different screen resolution. But when I run the exact same code on a laptop with an external monitor connected the box is too small. Laptop screen res is 1680x1050 and the monitor is 1920x1080.
pclRect has the same values no matter which computer I run it on.
Anybody's got an idea how to solve this?
Results:
void CForm::SetSize(CWnd *pCWnd, CRect *pclRect)
{
CDC *pclDC = m_pclPanel->GetDC();
CFont* font = pCWnd->GetFont();
LOGFONT logFont;
font->GetLogFont(&logFont);
CString str;
pCWnd->GetWindowText(str);//Get controller text
CClientDC dc(pCWnd);
dc.SelectObject(font);
int iWidth;
int iHeight;
long lFontSize = -MulDiv(logFont.lfHeight, GetDeviceCaps(pclDC->m_hDC, LOGPIXELSY), 72);
iWidth = dc.GetTextExtent(str).cx; //Get controller text length
iWidth += GetExtraWidth(); //This adds 18 to the width since it's the width of the checkbox itself
iHeight = abs(lFontSize) + GetExtraHeight();
pclRect->bottom = pclRect->top + iHeight;
pclRect->right = pclRect->left + iWidth;
pCWnd->MoveWindow(pclRect);
}
If target window is Vista or higher, use BCM_GETIDEALSIZE to find the minimum size. But check box cannot have multi-line flag (BS_MULTILINE). For example
m_checkBox.SetWindowText(L"long text xxx xxx xxx xxx xxx xxx");
SIZE sz;
if (Button_GetIdealSize(m_checkBox.m_hWnd, &sz) && sz.cx > 0 && sz.cy > 0)
{
m_checkBox.SetWindowPos(0, 0, 0, sz.cx, sz.cy, SWP_NOZORDER|SWP_NOMOVE);
}
else
{
//use another method ...
}
Otherwise, modify your code and instead of supplying 18 pixels for checkbox width, use
GetSystemMetrics to find the check box width (this results in 15 pixels in default DPI, so you have to add few more pixels for text padding).
Use GetThemePartSize if theme is active. For example:
CClientDC dc(this);
SIZE sz;
HTHEME ht = OpenThemeData(m_hWnd, L"Button");
if (ht)
{
GetThemePartSize(ht, dc, BP_CHECKBOX, CBS_CHECKEDNORMAL, NULL, TS_TRUE, &sz);
CloseThemeData(ht);
//sz.cx is 13 pixels in default DPI
}
else
{
sz.cx = GetSystemMetrics(SM_CXMENUCHECK);
//sz.cx is 15 pixels in default DPI
}
Screen resolution is not relevant here. The posted images suggest that both displays have the same DPI settings. Note that if DPI settings changes, and your application is DPI aware then sz.cx will be different.