|
You are dealing with TTF glyphs not a bitmap and you will need to understand how FUnits and the EMSquare work
TrueType fundamentals - Typography | Microsoft Docs[^]
The glyph is rendered to screen it never is a bitmap.
Now what you could do if font style isn't important is select a bitmap font and it will work much as you think.
Now if you want to do what you are doing with a TTF the function you want is called
GetGlyphOutlineA function | Microsoft Docs[^]
The DC will be your window you are drawing the text on so it matches one to one.
Now the problem is you will have an outline with beziers and lines and to be able to decide if you are in or out you need to scanline the contour at the y value and see if the point is between any two runline points of the scanline. Now if you follow all that this is the two scanline functions you need
short ScanlineLine2D(long x1, long y1,
long x2, long y2,
long sy,
long* px1){
long xt;
double t;
if (y2 == y1) return (0);
if (((sy >= y1) && (sy <= y2)) || ((sy >= y2) && (sy <= y1))){
t = ((double)(sy-y1)-FLT_EPSILON)/(double)(y2-y1);
if ( (t >= 0.0) && (t <= 1.0)) {
xt = (long)((double)(x2-x1)*t) + x1;
if (px1) (*px1) = xt;
return (1);
} else return (0);
} else return (0);
}
short ScanlineQuadBezier2D(long x1, long y1,
long x2, long y2,
long x3, long y3,
long sy,
long* px1, long* px2){
short rslt;
long dx0, dy0, dx1, dy1, dx2, dy2, xf;
long* px;
double tmf, tdf, tf, ymf;
dx0 = x2 - x1;
dy0 = y2 - y1;
dx1 = x3 - x2;
dy1 = y3 - y2;
if ((dx0 == 0) && (dy0 == 0) && (dx1 == 0) && (dy1 == 0))
return (0);
if ((dx0 == 0) && (dy0 == 0))
return (ScanlineLine2D(x2, y2, x3, y3, sy, px1));
if((dx1 == 0) && (dy1 == 0))
return (ScanlineLine2D(x1, y1, x2, y2, sy, px1));
dx2 = (dx1-dx0);
dy2 = (dy1-dy0);
if (dy2 != 0){
tmf = -(double)dy0/(double)dy2;
ymf = (double)y1 + FLT_EPSILON + tmf*(double)dy0;
tdf = ((double)sy - ymf)/(double)dy2;
if (tdf < 0.0) {
return (0);
} else if (tdf != 0.0) tdf = sqrt(tdf);
rslt = 0;
px = px1;
tf = tmf - tdf;
if ((tf >= 0.0) && (tf <= 1.0)){
xf = (long)(((double)dx2*tf + (double)(dx0*2))*tf) + x1;
(*px) = xf;
rslt++;
px = px2;
};
tf = tmf + tdf;
if ((tf >= 0.0) && (tf <= 1.0)){
xf = (long)(((double)dx2*tf + (double)(dx0*2))*tf) + x1;
(*px) = xf;
rslt++;
px = px2;
};
return (rslt);
} else if (dy0 != 0) {
tf = ((double)(sy - y1) - FLT_EPSILON)/(double)(dy0*2);
if ((tf >= 0.0) && (tf <= 1.0)){
xf = (long)(((double)dx2*tf + (double)(dx0*2))*tf) + x1;
(*px1) = xf;
return (1);
} else return (0);
} else return (0);
}
In vino veritas
|
|
|
|
|
I was actually able to make what I originally had in mind work - I know the glyphs aren't *defined* as bitmaps, but rendering a glyph into a device context with a bitmap selected effectively makes it into it into a bitmap, and that's what I did - but analyzing the glyph outlines directly as you suggest is surely more efficient, so I'll probably try it too.
|
|
|
|
|
Throw me an email to my personal account if you need a help. I have a pile of code for this stuff from CNC programs I have worked on for filling and inline/outlining the TTF fonts.
In vino veritas
|
|
|
|
|
The reason the location of the glyph in the memory device context bitmap wasn't what I had expected was that I had long ago set the text alignment of the window device context to TA_BASELINE, but that of the newly-created memory device context was set to something else.
|
|
|
|
|
Hi all,
I've had some preview on VS2019. They implemented a new feature, which places the application menu onto the title bar. Any thaughts on how to get this in an MFC application, too?
Regards,
Franz
|
|
|
|
|
Hi all
I have an MFC application that works perfectly with the CMDIChildWnd class, the frame is loaded and a splitterwnd is shown. I needed to create a new MFC application that uses the ribbon as the interface, and for that the only change I made was that my classes inherited from CMDIChildWndEx.
The two MFC applications should work with CMDIChildWndEx, but the old application does not show anything in the frames, it does not show errors, exceptions, it does not show inside the window. I already tried changing the style, forcing the frame size, changing the spliterwnd but nothing worked. See the LoadFrame code:
<pre lang="c++">void CMyFrame::ActivateFrame(int nCmdShow)
{
if (m_pMDIFrame && m_pMDIFrame->SendMessage(WM_IS_ENABLE_MDI_TAB))
{
LONG style = ::GetWindowLong(GetSafeHwnd(), GWL_STYLE) | WS_MAXIMIZE;
style &= ~WS_BORDER & ~WS_THICKFRAME & ~WS_DLGFRAME & ~WS_CAPTION;
::SetWindowLong(GetSafeHwnd(), GWL_STYLE, style);
style = ::GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
style &= ~WS_EX_CLIENTEDGE;
::SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, style);
ShowWindow(SW_SHOWMAXIMIZED);
return;
}
if (m_bInsideMDIFrame)
{
CMDIChildWndEx::ActivateFrame(nCmdShow);
return;
}
long style = ::GetWindowLong(GetSafeHwnd(), GWL_STYLE);
style &= ~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX;
::SetWindowLong(GetSafeHwnd(), GWL_STYLE, style);
style = ::GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
style &= ~WS_EX_CLIENTEDGE;
::SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, style);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
CFrameWnd::ActivateFrame(SW_SHOWNOACTIVATE);
}
BOOL CMyFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext* pContext)
{
if (g_globalVariables.GetParentWnd() == NULL)
{
if (m_bInsideMDIFrame)
return CMDIChildWndEx::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext);
return CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext);
}
ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
m_bInsideMDIFrame = FALSE;
m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE)
CString strFullString;
if (strFullString.LoadString(nIDResource))
AfxExtractSubString(m_strTitle, strFullString, 0); // first sub-string
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
dwDefaultStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
// attempt to create the window
LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
LPCTSTR lpszTitle = m_strTitle;
if (!CFrameWnd::Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, g_globalVariables.GetParentWnd(), 0, 0, pContext))
return FALSE; // will self destruct on failure normally
// save the default menu handle
ASSERT(m_hWnd != NULL);
m_hMenuDefault = ::GetMenu(m_hWnd);
// load accelerator resource
LoadAccelTable(MAKEINTRESOURCE(nIDResource));
if (pContext == NULL) // send initial update
SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
return TRUE;
}</pre>
Another difference is if application uses MDI tabs, in my tests everything indicates that CMDIChildWndEx does not work without the ribbon.
Has anyone had the same problem? Please, can anyone help me?
Thank you very much
|
|
|
|
|
I have about 1800 text files in a dir and their number is slowly growing (several files in a day). The size of a file is about 40 kB.
I need to enumerate all the file with the ".states" extension, open the file, check to see if there is a line that starts with a particular sequence of 4 chars and if the line exists, I save the line in a std::vector.
I use the following code, but it takes very long time at the first run (the subsequent runs are very fast):
struct _finddata_t fd; long hFile;
if((hFile=_findfirst("*.states", &fd))== -1L) return; do {
FILE *f= fopen(fd.name, "rS");
while(fgets(buf, sizeof(buf), f)) {
if(0 == _strnicmp(buf, "ABCD", 4)) {
Save buf in a std::vector
break;
}
}
fclose(f);
} while(_findnext(hFile, &fd) == 0);
_findclose(hFile);
Is there any way to speedup the code?
If I merge all the files in a single file, I solve the problem, but I prefer to keep all the original files.
|
|
|
|
|
Are the files that were in that folder on Monday (for example) still there on Tuesday? In other words, are you processing every file in that folder, or just the new ones?
Keep in mind that file I/O is arguably one of the slowest operations on a computer. It can be sped up to a marginal degree, but you are ultimately at the mercy of the disk.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
modified 31-Jan-19 12:11pm.
|
|
|
|
|
David Crow wrote: Are the files that were in that folder on Monday still there on Tuesday? Yes.
In other words, are you processing every file in that folder, or just the new ones? Every file in that folder.
|
|
|
|
|
So do the files that were processed on a Monday need to be processed again the next day?
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
|
|
|
|
|
David Crow wrote: So do the files that were processed on a Monday need to be processed again the next day? Yes. All those files can be thought as a database that I need to read every time I start the program.
|
|
|
|
|
Have you considered doing the processing in a (background) worker thread?
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
|
|
|
|
|
No, because when I start the program, I need to first process the files.
|
|
|
|
|
That fact does not negate the need/use of a worker thread. Part of the program's slowness may be that of perception. By having a responsive UI (not saying that yours is), the perception that the program is running slow is minimized.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
|
|
|
|
|
If you comment out the 'Save buf in a std::vector' code, is it still unacceptably slow?
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
jeron1 wrote: If you comment out the 'Save buf in a std::vector' code, is it still unacceptably slow? Yes; that code takes a totally negligible amount of time.
If I only do the search, without opening the file, it's very fast, but if I also open the file, the code is terribly slow.
|
|
|
|
|
Member 3648633 wrote:
If I only do the search, without opening the file How can you search the contents of a file without first opening it?
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"You can easily judge the character of a man by how he treats those who can do nothing for him." - James D. Miles
modified 31-Jan-19 12:14pm.
|
|
|
|
|
For "search" I meant the file enumeration with _findnext().
|
|
|
|
|
Maybe read the whole file at once as opposed to many fgets() calls, then do your string search in RAM?
"the debugger doesn't tell me anything because this code compiles just fine" - random QA comment
"Facebook is where you tell lies to your friends. Twitter is where you tell the truth to strangers." - chriselst
"I don't drink any more... then again, I don't drink any less." - Mike Mullikins uncle
|
|
|
|
|
I tried that method, but nothing changes.
Any kind of file opening (including MapViewOfFile) terribly slows the process down.
|
|
|
|
|
Hi,
Some comments:
1.) Reading files may be slightly faster if you read using a multiple of the drive sector size or read it all at once.
2.) If you've ever wondered why file-backed cache implementations save files into a hierarchical folder structure it's because enumerating 10,000 files in a single folder may cause a small performance hit. If you plan on storing many thousands of files you may want to design a folder structure. Maybe something simple such as alphabetical A-C, D-F ... or something based on timestamp. This is not much of an issue on a modern SSD but old spindle drives take a performance hit.
3.) The code you have shown above is reading the file contents into a local buffer. You would get a huge performance boost by using the MapViewOfFile function to map the file directly into your process space.
Have a look at the Creating a View Within a File sample. This sample is demonstrating how to take a large file and map only 1kb at a time into your process. Don't do that.
You stated that your files are around ~40 kb so I'd recommend mapping the entire file into your process address space. I'd also recommend using two file mappings. While FileA is being processed you can have the operating system map FileB into your process. This would mitigate any latency caused by the i/o subsystem.
The majority of your latency is between opening files. I highly recommend the second file mapping.
Best Wishes,
-David Delaune
|
|
|
|
|
I tried with 1 file mapping only; it works but the speed is exactly the same.
Probably the only way is to merge all the file in one big file. That way I can also optimize the file format for my needs.
Thanks you all.
|
|
|
|
|
The more obvious answer is get whatever is saving the files to put the ones which contain your string ABCD out under a special name string. Then you don't have to search inside the file at all to find the files you want. Another obvious choice is have the files on a ramdisk as there isn't much data.
The whole process seems a bit backward to me you are working on the reading code not the writing code.
In vino veritas
modified 31-Jan-19 21:00pm.
|
|
|
|
|
That's a good approach, but as I understand it the main task is not to find files that contain the string, but find all lines within these files containing it. A specific kind of filename wouldn't be enough.
Your suggestion to include the writing into the problem solution is a good idea. However, if we do that, we might as well write all the data to a database. Retrieving the correct lines would then only require a simple SQL query.
GOTOs are a bit like wire coat hangers: they tend to breed in the darkness, such that where there once were few, eventually there are many, and the program's architecture collapses beneath them. (Fran Poretto)
|
|
|
|
|
You only need one instance to flag it as special who cares how many times it writes the special sequence after that. The process is to eliminate the mass of files that aren't of any interest by using the name.
I am not changing anything other than the name of the file .. hardly complex or rocket science and much easier and much faster than a database connection
In vino veritas
|
|
|
|
|