static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) { uiTableValue *value; WCHAR *wstr; bool enabled; HTHEME theme; RECT r; TEXTMETRICW tm; if (hr != S_OK) return hr; if (s->p->buttonModelColumn == -1) return S_OK; value = uiprivTableModelCellValue(s->model, s->iItem, s->p->buttonModelColumn); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); enabled = uiprivTableModelCellEditable(s->model, s->iItem, s->p->buttonClickableModelColumn); theme = OpenThemeData(s->t->hwnd, L"button"); if (GetTextMetricsW(s->dc, &tm) == 0) { logLastError(L"GetTextMetricsW()"); hr = E_FAIL; goto fail; } r = s->m->subitemBounds; if (theme != NULL) { int state; state = PBS_NORMAL; if (!enabled) state = PBS_DISABLED; hr = DrawThemeBackground(theme, s->dc, BP_PUSHBUTTON, state, &r, NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground()", hr); goto fail; } // TODO DT_EDITCONTROL? // TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here // TODO is there a theme property we can get instead of hardcoding these flags? if not, make these flags a macro hr = DrawThemeText(theme, s->dc, BP_PUSHBUTTON, state, wstr, -1, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX, 0, &r); if (hr != S_OK) { logHRESULT(L"DrawThemeText()", hr); goto fail; } } else { UINT state; HBRUSH color, prevColor; int prevBkMode; // TODO check errors // TODO explain why we're not doing this in the themed case (it has to do with extra transparent pixels) InflateRect(&r, -1, -1); state = DFCS_BUTTONPUSH; if (!enabled) state |= DFCS_INACTIVE; if (DrawFrameControl(s->dc, &r, DFC_BUTTON, state) == 0) { logLastError(L"DrawFrameControl()"); hr = E_FAIL; goto fail; } color = GetSysColorBrush(COLOR_BTNTEXT); // TODO check errors for these two prevColor = (HBRUSH) SelectObject(s->dc, color); prevBkMode = SetBkMode(s->dc, TRANSPARENT); // TODO DT_EDITCONTROL? // TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here if (DrawTextW(s->dc, wstr, -1, &r, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX) == 0) { logLastError(L"DrawTextW()"); hr = E_FAIL; goto fail; } // TODO check errors for these two SetBkMode(s->dc, prevBkMode); SelectObject(s->dc, prevColor); } hr = S_OK; fail: // TODO check errors if (theme != NULL) CloseThemeData(theme); uiprivFree(wstr); return hr; }
/* * @implemented * * Synced with wine 1.1.32 */ INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count, LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ) { SIZE size; const WCHAR *strPtr; WCHAR *retstr, *p_retstr; size_t size_retstr; WCHAR line[MAX_BUFFER]; int len, lh, count=i_count; TEXTMETRICW tm; int lmargin = 0, rmargin = 0; int x = rect->left, y = rect->top; int width = rect->right - rect->left; int max_width = 0; int last_line; int tabwidth /* to keep gcc happy */ = 0; int prefix_offset; ellipsis_data ellip; int invert_y=0; TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str, count), count, wine_dbgstr_rect(rect), flags); if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n", dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin); if (!str) return 0; strPtr = str; if (flags & DT_SINGLELINE) flags &= ~DT_WORDBREAK; GetTextMetricsW(hdc, &tm); if (flags & DT_EXTERNALLEADING) lh = tm.tmHeight + tm.tmExternalLeading; else lh = tm.tmHeight; if (str[0] && count == 0) return lh; if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS)) return 0; if (count == -1) { count = strlenW(str); if (count == 0) { if( flags & DT_CALCRECT) { rect->right = rect->left; if( flags & DT_SINGLELINE) rect->bottom = rect->top + lh; else rect->bottom = rect->top; } return lh; } } if (GetGraphicsMode(hdc) == GM_COMPATIBLE) { SIZE window_ext, viewport_ext; GetWindowExtEx(hdc, &window_ext); GetViewportExtEx(hdc, &viewport_ext); if ((window_ext.cy > 0) != (viewport_ext.cy > 0)) invert_y = 1; } if (dtp) { lmargin = dtp->iLeftMargin; rmargin = dtp->iRightMargin; if (!(flags & (DT_CENTER | DT_RIGHT))) x += lmargin; dtp->uiLengthDrawn = 0; /* This param RECEIVES number of chars processed */ } if (flags & DT_EXPANDTABS) { int tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8; tabwidth = tm.tmAveCharWidth * tabstop; } if (flags & DT_CALCRECT) flags |= DT_NOCLIP; if (flags & DT_MODIFYSTRING) { size_retstr = (count + 4) * sizeof (WCHAR); retstr = HeapAlloc(GetProcessHeap(), 0, size_retstr); if (!retstr) return 0; memcpy (retstr, str, size_retstr); } else { size_retstr = 0; retstr = NULL; } p_retstr = retstr; do { len = sizeof(line)/sizeof(line[0]); if (invert_y) last_line = !(flags & DT_NOCLIP) && y - ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) < rect->bottom; else last_line = !(flags & DT_NOCLIP) && y + ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) > rect->bottom; strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags, &size, last_line, &p_retstr, tabwidth, &prefix_offset, &ellip); if (flags & DT_CENTER) x = (rect->left + rect->right - size.cx) / 2; else if (flags & DT_RIGHT) x = rect->right - size.cx; if (flags & DT_SINGLELINE) { if (flags & DT_VCENTER) y = rect->top + (rect->bottom - rect->top) / 2 - size.cy / 2; else if (flags & DT_BOTTOM) y = rect->bottom - size.cy; } if (!(flags & DT_CALCRECT)) { const WCHAR *str = line; int xseg = x; while (len) { int len_seg; SIZE size; if ((flags & DT_EXPANDTABS)) { const WCHAR *p; p = str; while (p < str+len && *p != TAB) p++; len_seg = p - str; if (len_seg != len && !GetTextExtentPointW(hdc, str, len_seg, &size)) return 0; } else len_seg = len; if (!ExtTextOutW( hdc, xseg, y, ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) | ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0), rect, str, len_seg, NULL )) return 0; if (prefix_offset != -1 && prefix_offset < len_seg) { TEXT_DrawUnderscore (hdc, xseg, y + tm.tmAscent + 1, str, prefix_offset, (flags & DT_NOCLIP) ? NULL : rect); } len -= len_seg; str += len_seg; if (len) { assert ((flags & DT_EXPANDTABS) && *str == TAB); len--; str++; xseg += ((size.cx/tabwidth)+1)*tabwidth; if (prefix_offset != -1) { if (prefix_offset < len_seg) { /* We have just drawn an underscore; we ought to * figure out where the next one is. I am going * to leave it for now until I have a better model * for the line, which will make reprefixing easier. * This is where ellip would be used. */ prefix_offset = -1; } else prefix_offset -= len_seg; } } } } else if (size.cx > max_width) max_width = size.cx; if (invert_y) y -= lh; else y += lh; if (dtp) dtp->uiLengthDrawn += len; } while (strPtr && !last_line); if (flags & DT_CALCRECT) { rect->right = rect->left + max_width; rect->bottom = y; if (dtp) rect->right += lmargin + rmargin; } if (retstr) { memcpy (str, retstr, size_retstr); HeapFree (GetProcessHeap(), 0, retstr); } return y - rect->top; }
static HRESULT drawProgressBarPart(HRESULT hr, struct drawState *s) { int progress; LONG indeterminatePos; HTHEME theme; RECT r; RECT rBorder, rFill[2]; int i, nFill; TEXTMETRICW tm; int sysColor; if (hr != S_OK) return hr; if (s->p->progressBarModelColumn == -1) return S_OK; progress = uiprivTableProgress(s->t, s->iItem, s->iSubItem, s->p->progressBarModelColumn, &indeterminatePos); theme = OpenThemeData(s->t->hwnd, L"PROGRESS"); if (GetTextMetricsW(s->dc, &tm) == 0) { logLastError(L"GetTextMetricsW()"); hr = E_FAIL; goto fail; } r = s->m->subitemBounds; // this sets the height of the progressbar and vertically centers it in one fell swoop r.top += (r.bottom - tm.tmHeight - r.top) / 2; r.bottom = r.top + tm.tmHeight; // TODO check errors rBorder = r; InflateRect(&rBorder, -1, -1); if (theme != NULL) { RECT crect; hr = GetThemeBackgroundContentRect(theme, s->dc, PP_TRANSPARENTBAR, PBBS_NORMAL, &rBorder, &crect); if (hr != S_OK) { logHRESULT(L"GetThemeBackgroundContentRect()", hr); goto fail; } hr = DrawThemeBackground(theme, s->dc, PP_TRANSPARENTBAR, PBBS_NORMAL, &crect, NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground() border", hr); goto fail; } } else { HPEN pen, prevPen; HBRUSH brush, prevBrush; sysColor = COLOR_HIGHLIGHT; if (s->m->selected) sysColor = COLOR_HIGHLIGHTTEXT; // TODO check errors everywhere pen = CreatePen(PS_SOLID, 1, GetSysColor(sysColor)); prevPen = (HPEN) SelectObject(s->dc, pen); brush = (HBRUSH) GetStockObject(NULL_BRUSH); prevBrush = (HBRUSH) SelectObject(s->dc, brush); Rectangle(s->dc, rBorder.left, rBorder.top, rBorder.right, rBorder.bottom); SelectObject(s->dc, prevBrush); SelectObject(s->dc, prevPen); DeleteObject(pen); } nFill = 1; rFill[0] = r; // TODO check error InflateRect(&rFill[0], -1, -1); if (progress != -1) rFill[0].right -= (rFill[0].right - rFill[0].left) * (100 - progress) / 100; else { LONG barWidth; LONG pieceWidth; // TODO explain all this // TODO this should really start the progressbar scrolling into view instead of already on screen when first set rFill[1] = rFill[0]; // save in case we need it barWidth = rFill[0].right - rFill[0].left; pieceWidth = barWidth / indeterminateSegments; rFill[0].left += indeterminatePos % barWidth; if ((rFill[0].left + pieceWidth) >= rFill[0].right) { // make this piece wrap back around nFill++; rFill[1].right = rFill[1].left + (pieceWidth - (rFill[0].right - rFill[0].left)); } else rFill[0].right = rFill[0].left + pieceWidth; } for (i = 0; i < nFill; i++) if (theme != NULL) { hr = DrawThemeBackground(theme, s->dc, PP_FILL, PBFS_NORMAL, &rFill[i], NULL); if (hr != S_OK) { logHRESULT(L"DrawThemeBackground() fill", hr); goto fail; } } else // TODO check errors FillRect(s->dc, &rFill[i], GetSysColorBrush(sysColor)); hr = S_OK; fail: // TODO check errors if (theme != NULL) CloseThemeData(theme); return hr; }
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; unsigned char fail_type = 0; // Initialize GDI+. Gdiplus::GdiplusStartup( &gdiplusToken, &gdiplusStartupInput, NULL ); // Blocks our reading thread and various GUI operations. InitializeCriticalSection( &pe_cs ); // Get the default message system font. NONCLIENTMETRICS ncm = { NULL }; ncm.cbSize = sizeof( NONCLIENTMETRICS ); SystemParametersInfo( SPI_GETNONCLIENTMETRICS, sizeof( NONCLIENTMETRICS ), &ncm, 0 ); // Set our global font to the LOGFONT value obtained from the system. hFont = CreateFontIndirect( &ncm.lfMessageFont ); // Get the row height for our listview control. TEXTMETRIC tm; HDC hDC = GetDC( NULL ); HFONT ohf = ( HFONT )SelectObject( hDC, hFont ); GetTextMetricsW( hDC, &tm ); SelectObject( hDC, ohf ); // Reset old font. ReleaseDC( NULL, hDC ); row_height = tm.tmHeight + tm.tmExternalLeading + 5; int icon_height = GetSystemMetrics( SM_CYSMICON ) + 2; if ( row_height < icon_height ) { row_height = icon_height; } // Get the system icon for each of the three file types. SHFILEINFOA shfi = { NULL }; SHGetFileInfoA( ".bmp", FILE_ATTRIBUTE_NORMAL, &shfi, sizeof( shfi ), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES ); hIcon_bmp = shfi.hIcon; SHGetFileInfoA( ".png", FILE_ATTRIBUTE_NORMAL, &shfi, sizeof( shfi ), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES ); hIcon_png = shfi.hIcon; SHGetFileInfoA( ".jpg", FILE_ATTRIBUTE_NORMAL, &shfi, sizeof( shfi ), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES ); hIcon_jpg = shfi.hIcon; // Initialize our window class. WNDCLASSEX wcex; wcex.cbSize = sizeof( WNDCLASSEX ); wcex.style = CS_VREDRAW | CS_HREDRAW; wcex.lpfnWndProc = MainWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_ICON ) ); wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW ); wcex.lpszMenuName = NULL; wcex.lpszClassName = L"thumbcache"; wcex.hIconSm = LoadIcon( wcex.hInstance, MAKEINTRESOURCE( IDI_APPLICATION ) ); if ( !RegisterClassEx( &wcex ) ) { fail_type = 1; goto CLEANUP; } wcex.lpfnWndProc = ImageWndProc; wcex.lpszClassName = L"image"; if ( !RegisterClassEx( &wcex ) ) { fail_type = 1; goto CLEANUP; } wcex.lpfnWndProc = ScanWndProc; wcex.lpszClassName = L"scan"; if ( !RegisterClassEx( &wcex ) ) { fail_type = 1; goto CLEANUP; } wcex.lpfnWndProc = InfoWndProc; wcex.lpszClassName = L"info"; if ( !RegisterClassEx( &wcex ) ) { fail_type = 1; goto CLEANUP; } wcex.lpfnWndProc = PropertyWndProc; wcex.lpszClassName = L"property"; if ( !RegisterClassEx( &wcex ) ) { fail_type = 1; goto CLEANUP; } g_hWnd_main = CreateWindow( L"thumbcache", PROGRAM_CAPTION, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, ( ( GetSystemMetrics( SM_CXSCREEN ) - MIN_WIDTH ) / 2 ), ( ( GetSystemMetrics( SM_CYSCREEN ) - MIN_HEIGHT ) / 2 ), MIN_WIDTH, MIN_HEIGHT, NULL, NULL, NULL, NULL ); if ( !g_hWnd_main ) { fail_type = 2; goto CLEANUP; } g_hWnd_image = CreateWindow( L"image", PROGRAM_CAPTION, WS_OVERLAPPEDWINDOW, ( ( GetSystemMetrics( SM_CXSCREEN ) - MIN_WIDTH ) / 2 ), ( ( GetSystemMetrics( SM_CYSCREEN ) - MIN_HEIGHT ) / 2 ), MIN_HEIGHT, MIN_HEIGHT, NULL, NULL, NULL, NULL ); if ( !g_hWnd_image ) { fail_type = 2; goto CLEANUP; } g_hWnd_scan = CreateWindow( L"scan", L"Map File Paths to Cache Entry Hashes", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_CLIPCHILDREN, ( ( GetSystemMetrics( SM_CXSCREEN ) - MIN_WIDTH ) / 2 ), ( ( GetSystemMetrics( SM_CYSCREEN ) - ( MIN_HEIGHT - 115 ) ) / 2 ), MIN_WIDTH, ( MIN_HEIGHT - 115 ), g_hWnd_main, NULL, NULL, NULL ); if ( !g_hWnd_scan ) { fail_type = 2; goto CLEANUP; } g_hWnd_info = CreateWindow( L"info", L"Extended Information", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, ( ( GetSystemMetrics( SM_CXSCREEN ) - MIN_WIDTH ) / 2 ), ( ( GetSystemMetrics( SM_CYSCREEN ) - MIN_WIDTH ) / 2 ), MIN_WIDTH, MIN_WIDTH, NULL, NULL, NULL, NULL ); if ( !g_hWnd_info ) { fail_type = 2; goto CLEANUP; } g_hWnd_property = CreateWindow( L"property", L"Property Value", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, ( ( GetSystemMetrics( SM_CXSCREEN ) - MIN_HEIGHT ) / 2 ), ( ( GetSystemMetrics( SM_CYSCREEN ) - MIN_HEIGHT ) / 2 ), MIN_HEIGHT, MIN_HEIGHT, NULL, NULL, NULL, NULL ); if ( !g_hWnd_property ) { fail_type = 2; goto CLEANUP; } // See if we have any command-line parameters if ( lpCmdLine != NULL && lpCmdLine[ 0 ] != NULL ) { // Count the number of parameters and split them into an array. int argCount = 0; LPWSTR *szArgList = CommandLineToArgvW( GetCommandLine(), &argCount ); if ( szArgList != NULL ) { // The first parameter is the path to the executable. Ignore it. if ( argCount > 1 ) { // Allocate enough space to hold each parameter. They should all be full paths. pathinfo *pi = ( pathinfo * )malloc( sizeof( pathinfo ) ); pi->type = 0; pi->offset = 0; pi->output_path = NULL; pi->filepath = ( wchar_t * )malloc( sizeof( wchar_t ) * ( ( MAX_PATH * ( argCount - 1 ) ) + 1 ) ); wmemset( pi->filepath, 0, ( ( MAX_PATH * ( argCount - 1 ) ) + 1 ) ); cmd_line = 1; // Open the database(s) from the command-line. int filepath_offset = 0; for ( int i = 1; i < argCount; ++i ) { int filepath_length = wcslen( szArgList[ i ] ); // See if it's an output parameter. if ( filepath_length > 1 && szArgList[ i ][ 0 ] == L'-' && ( szArgList[ i ][ 1 ] == L'o' || szArgList[ i ][ 1 ] == L'O' ) ) { // See if the next parameter exists. We'll assume it's the output directory. if ( i + 1 < argCount ) { if ( pi->output_path != NULL ) { free( pi->output_path ); } pi->output_path = _wcsdup( szArgList[ ++i ] ); pi->type = 0; cmd_line = 2; // Save the database(s) from the command-line. Do not display the main window or any prompts. } } else if ( filepath_length > 1 && szArgList[ i ][ 0 ] == L'-' && ( szArgList[ i ][ 1 ] == L'c' || szArgList[ i ][ 1 ] == L'C' ) ) { // See if the next parameter exists. We'll assume it's the output directory. if ( i + 1 < argCount ) { if ( pi->output_path != NULL ) { free( pi->output_path ); } pi->output_path = _wcsdup( szArgList[ ++i ] ); pi->type = 1; cmd_line = 2; // Save the database(s) from the command-line. Do not display the main window or any prompts. } } else if ( filepath_length > 1 && szArgList[ i ][ 0 ] == L'-' && ( szArgList[ i ][ 1 ] == L'z' || szArgList[ i ][ 1 ] == L'Z' ) ) { hide_blank_entries = true; // Put a check next to the menu item. g_hMenu is created in g_hWnd_main. CheckMenuItem( g_hMenu, MENU_HIDE_BLANK, MF_CHECKED ); } else // Copy the paths into the NULL separated filepath. { // If the user typed a relative path, get the full path. wchar_t full_path[ MAX_PATH ] = { 0 }; DWORD full_path_length = min( GetFullPathName( szArgList[ i ], MAX_PATH, full_path, NULL ), MAX_PATH ); wmemcpy_s( pi->filepath + filepath_offset, ( ( MAX_PATH * ( argCount - 1 ) ) + 1 ) - filepath_offset, full_path, full_path_length + 1 ); filepath_offset += ( full_path_length + 1 ); } } // Only read the database if there's a file to open. if ( pi->filepath[ 0 ] != NULL ) { // filepath will be freed in the thread. CloseHandle( ( HANDLE )_beginthreadex( NULL, 0, &read_thumbcache, ( void * )pi, 0, NULL ) ); } else { // Cleanup our parameters structure and exit the program. free( pi->output_path ); free( pi->filepath ); free( pi ); cmd_line = -1; SendMessage( g_hWnd_main, WM_DESTROY_ALT, 0, 0 ); } } // Free the parameter list. LocalFree( szArgList ); } } if ( cmd_line == 0 || cmd_line == 1 ) { ShowWindow( g_hWnd_main, SW_SHOW ); } // Main message loop: MSG msg; while ( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { if ( g_hWnd_active == NULL || !IsDialogMessage( g_hWnd_active, &msg ) ) // Checks tab stops. { TranslateMessage( &msg ); DispatchMessage( &msg ); } } CLEANUP: // Destroy our icons. DestroyIcon( hIcon_jpg ); DestroyIcon( hIcon_png ); DestroyIcon( hIcon_bmp ); // Delete our font. DeleteObject( hFont ); // Delete our critical section. DeleteCriticalSection( &pe_cs ); // Shutdown GDI+ Gdiplus::GdiplusShutdown( gdiplusToken ); // Unload the modules if they were initialized. UnInitializeMsSrch(); UnInitializeMsSCB(); if ( fail_type == 1 ) { MessageBoxA( NULL, "Call to RegisterClassEx failed!", PROGRAM_CAPTION_A, MB_ICONWARNING ); } else if ( fail_type == 2 ) { MessageBoxA( NULL, "Call to CreateWindow failed!", PROGRAM_CAPTION_A, MB_ICONWARNING ); } return ( int )msg.wParam; }
static BOOL WINAPI ID3DXFontImpl_GetTextMetricsW(ID3DXFont *iface, TEXTMETRICW *metrics) { struct d3dx_font *This = impl_from_ID3DXFont(iface); TRACE("iface %p, metrics %p\n", iface, metrics); return GetTextMetricsW(This->hdc, metrics); }
NTSTATUS EnumerateFonts( DWORD Flags) { TEXTMETRIC tmi; HDC hDC; PFACENODE pFN; ULONG ulOldEnumFilter; DWORD FontIndex; DWORD dwFontType = 0; DBGFONTS(("EnumerateFonts %lx\n", Flags)); dwFontType = (EF_TTFONT|EF_OEMFONT|EF_DEFFACE) & Flags; if (FontInfo == NULL) { // // allocate memory for the font array // NumberOfFonts = 0; FontInfo = (PFONT_INFO)HeapAlloc(pConHeap,MAKE_TAG( FONT_TAG ),sizeof(FONT_INFO) * INITIAL_FONTS); if (FontInfo == NULL) return STATUS_NO_MEMORY; FontInfoLength = INITIAL_FONTS; } hDC = CreateDCW(L"DISPLAY",NULL,NULL,NULL); // Before enumeration, turn off font enumeration filters. ulOldEnumFilter = SetFontEnumeration(0); // restore all the other flags SetFontEnumeration(ulOldEnumFilter & ~FE_FILTER_TRUETYPE); if (Flags & EF_DEFFACE) { SelectObject(hDC,GetStockObject(OEM_FIXED_FONT)); if (GetTextMetricsW(hDC, &tmi)) { DefaultFontSize.X = (SHORT)(tmi.tmMaxCharWidth); DefaultFontSize.Y = (SHORT)(tmi.tmHeight+tmi.tmExternalLeading); DefaultFontFamily = tmi.tmPitchAndFamily; } GetTextFaceW(hDC, LF_FACESIZE, DefaultFaceName); DBGFONTS(("Default (OEM) Font %ls (%d,%d)\n", DefaultFaceName, DefaultFontSize.X, DefaultFontSize.Y)); // Make sure we are going to enumerate the OEM face. pFN = AddFaceNode(&gpFaceNames, DefaultFaceName); pFN->dwFlag |= EF_DEFFACE | EF_OEMFONT; } // Use DoFontEnum to get all fonts from the system. Our FontEnum // proc puts just the ones we want into an array // for (pFN = gpFaceNames; pFN; pFN = pFN->pNext) { DBGFONTS(("\"%ls\" is %s%s%s%s%s%s\n", pFN->awch, pFN->dwFlag & EF_NEW ? "NEW " : " ", pFN->dwFlag & EF_OLD ? "OLD " : " ", pFN->dwFlag & EF_ENUMERATED ? "ENUMERATED " : " ", pFN->dwFlag & EF_OEMFONT ? "OEMFONT " : " ", pFN->dwFlag & EF_TTFONT ? "TTFONT " : " ", pFN->dwFlag & EF_DEFFACE ? "DEFFACE " : " ")); if ((pFN->dwFlag & dwFontType) == 0) { // not the kind of face we want continue; } if (pFN->dwFlag & EF_ENUMERATED) { // we already enumerated this face continue; } DoFontEnum(hDC, pFN->awch, DefaultFontSize.Y); pFN->dwFlag |= EF_ENUMERATED; } // After enumerating fonts, restore the font enumeration filter. SetFontEnumeration(ulOldEnumFilter); DeleteDC(hDC); // Make sure the default font is set correctly if (NumberOfFonts > 0 && DefaultFontSize.X == 0 && DefaultFontSize.Y == 0) { DefaultFontSize.X = FontInfo[0].Size.X; DefaultFontSize.Y = FontInfo[0].Size.Y; DefaultFontFamily = FontInfo[0].Family; } for (FontIndex = 0; FontIndex < NumberOfFonts; FontIndex++) { if (FontInfo[FontIndex].Size.X == DefaultFontSize.X && FontInfo[FontIndex].Size.Y == DefaultFontSize.Y && FontInfo[FontIndex].Family == DefaultFontFamily) { break; } } ASSERT(FontIndex < NumberOfFonts); if (FontIndex < NumberOfFonts) { DefaultFontIndex = FontIndex; } else { DefaultFontIndex = 0; } DBGFONTS(("EnumerateFonts : DefaultFontIndex = %ld\n", DefaultFontIndex)); return STATUS_SUCCESS; }
/* * Returns bit combination * FE_ABANDONFONT - do not continue enumerating this font * FE_FONTOK - font was created and added to cache or already there */ int CALLBACK FontEnum( LPENUMLOGFONTW lpLogFont, LPNEWTEXTMETRICW lpTextMetric, int nFontType, LPARAM lParam ) /*++ Is called exactly once by GDI for each font in the system. This routine is used to store the FONT_INFO structure. --*/ { PFONTENUMDC pfed = (PFONTENUMDC)lParam; HDC hDC = pfed->hDC; BOOL bFindFaces = pfed->bFindFaces; HFONT hFont; TEXTMETRICW tmi; LONG nFont; LONG nFontNew; COORD SizeToShow; COORD SizeActual; COORD SizeWant; BYTE tmFamily; SIZE Size; LPWSTR pwszFace = lpLogFont->elfLogFont.lfFaceName; PFACENODE pFN; DBGFONTS((" FontEnum \"%ls\" (%d,%d) weight 0x%lx(%d) -- %s\n", pwszFace, lpLogFont->elfLogFont.lfWidth, lpLogFont->elfLogFont.lfHeight, lpLogFont->elfLogFont.lfWeight, lpLogFont->elfLogFont.lfWeight, bFindFaces ? "Finding Faces" : "Creating Fonts")); // // reject variable width and italic fonts, also tt fonts with neg ac // also reject DBCS fonts because they are never really fixed pitch // they can be "binary" pitch meaning SBCS widths are the same as all // other SBCS widths and DBCS widhts the same as all DBCS widhts // (but DBCS widths won't be the same as SBCS widths) if ( !(lpLogFont->elfLogFont.lfPitchAndFamily & FIXED_PITCH) || (lpLogFont->elfLogFont.lfItalic) || !(((NTMW_INTERNAL *)lpTextMetric)->tmd.fl & TMD_NONNEGATIVE_AC) || (IS_ANY_DBCS_CHARSET(lpLogFont->elfLogFont.lfCharSet)) ) { DBGFONTS((" REJECT face (variable pitch, italic, or neg a&c)\n")); return bFindFaces ? TRUE : FALSE; // unsuitable font } if (nFontType == TRUETYPE_FONTTYPE) { lpLogFont->elfLogFont.lfHeight = pfed->TTPointSize; lpLogFont->elfLogFont.lfWidth = 0; lpLogFont->elfLogFont.lfWeight = FW_NORMAL; } /* * reject TT fonts for whoom family is not modern, that is do not use * FF_DONTCARE // may be surprised unpleasantly * FF_DECORATIVE // likely to be symbol fonts * FF_SCRIPT // cursive, inappropriate for console * FF_SWISS OR FF_ROMAN // variable pitch */ if ((nFontType == TRUETYPE_FONTTYPE) && ((lpLogFont->elfLogFont.lfPitchAndFamily & 0xf0) != FF_MODERN)) { DBGFONTS((" REJECT face (TT but not FF_MODERN)\n")); return bFindFaces ? TRUE : FALSE; // unsuitable font } /* * reject non-TT fonts that aren't OEM */ if ((nFontType != TRUETYPE_FONTTYPE) && (lpLogFont->elfLogFont.lfCharSet != OEM_CHARSET)) { DBGFONTS((" REJECT face (not TT nor OEM)\n")); return bFindFaces ? TRUE : FALSE; // unsuitable font } /* * Add or find the facename */ pFN = AddFaceNode(&gpFaceNames, pwszFace); if (pFN == NULL) { return FALSE; } if (bFindFaces) { if (nFontType == TRUETYPE_FONTTYPE) { DBGFONTS(("NEW TT FACE %ls\n", pwszFace)); pFN->dwFlag |= EF_TTFONT; } else if (nFontType == RASTER_FONTTYPE) { DBGFONTS(("NEW OEM FACE %ls\n",pwszFace)); pFN->dwFlag |= EF_OEMFONT; } return 0; } if (IS_BOLD(lpLogFont->elfLogFont.lfWeight)) { DBGFONTS2((" A bold font (weight %d)\n", lpLogFont->elfLogFont.lfWeight)); // return 0; } /* get font info */ SizeWant.Y = (SHORT)lpLogFont->elfLogFont.lfHeight; SizeWant.X = (SHORT)lpLogFont->elfLogFont.lfWidth; CreateBoldFont: lpLogFont->elfLogFont.lfQuality = NONANTIALIASED_QUALITY; hFont = CreateFontIndirectW(&lpLogFont->elfLogFont); ASSERT(hFont); if (!hFont) { DBGFONTS((" REJECT font (can't create)\n")); return 0; // same font in other sizes may still be suitable } DBGFONTS2((" hFont = %lx\n", hFont)); // // for reasons unbeknownst to me, removing this code causes GDI // to yack, claiming that the font is owned by another process. // SelectObject(hDC,hFont); if (!GetTextMetricsW(hDC, &tmi)) { tmi = *((LPTEXTMETRICW)lpTextMetric); } if (GetTextExtentPoint32W(hDC, L"0", 1, &Size)) { SizeActual.X = (SHORT)Size.cx; } else { SizeActual.X = (SHORT)(tmi.tmMaxCharWidth); } SizeActual.Y = (SHORT)(tmi.tmHeight + tmi.tmExternalLeading); DBGFONTS2((" actual size %d,%d\n", SizeActual.X, SizeActual.Y)); tmFamily = tmi.tmPitchAndFamily; if (TM_IS_TT_FONT(tmFamily) && (SizeWant.Y >= 0)) { SizeToShow = SizeWant; if (SizeWant.X == 0) { // Asking for zero width height gets a default aspect-ratio width // It's better to show that width rather than 0. SizeToShow.X = SizeActual.X; } } else { SizeToShow = SizeActual; } DBGFONTS2((" SizeToShow = (%d,%d), SizeActual = (%d,%d)\n", SizeToShow.X, SizeToShow.Y, SizeActual.X, SizeActual.Y)); // there's a GDI bug - this assert fails occasionally //ASSERT (tmi.tmw.tmMaxCharWidth == lpTextMetric->tmMaxCharWidth); /* * NOW, determine whether this font entry has already been cached * LATER : it may be possible to do this before creating the font, if * we can trust the dimensions & other info from lpTextMetric. * Sort by size: * 1) By pixelheight (negative Y values) * 2) By height (as shown) * 3) By width (as shown) */ for (nFont = 0; nFont < (LONG)NumberOfFonts; ++nFont) { COORD SizeShown; if (FontInfo[nFont].hFont == NULL) { DBGFONTS(("! Font %x has a NULL hFont\n", nFont)); continue; } if (FontInfo[nFont].SizeWant.X > 0) { SizeShown.X = FontInfo[nFont].SizeWant.X; } else { SizeShown.X = FontInfo[nFont].Size.X; } if (FontInfo[nFont].SizeWant.Y > 0) { // This is a font specified by cell height. SizeShown.Y = FontInfo[nFont].SizeWant.Y; } else { SizeShown.Y = FontInfo[nFont].Size.Y; if (FontInfo[nFont].SizeWant.Y < 0) { // This is a TT font specified by character height. if (SizeWant.Y < 0 && SizeWant.Y > FontInfo[nFont].SizeWant.Y) { // Requested pixelheight is smaller than this one. DBGFONTS(("INSERT %d pt at %x, before %d pt\n", -SizeWant.Y, nFont, -FontInfo[nFont].SizeWant.Y)); nFontNew = nFont; goto InsertNewFont; } } } // DBGFONTS((" SizeShown(%x) = (%d,%d)\n",nFont,SizeShown.X,SizeShown.Y)); if (SIZE_EQUAL(SizeShown, SizeToShow) && FontInfo[nFont].Family == tmFamily && FontInfo[nFont].Weight == tmi.tmWeight && wcscmp(FontInfo[nFont].FaceName, pwszFace) == 0) { /* * Already have this font */ DBGFONTS2((" Already have the font\n")); DeleteObject(hFont); pfed->ulFE |= FE_FONTOK; return TRUE; } if ((SizeToShow.Y < SizeShown.Y) || (SizeToShow.Y == SizeShown.Y && SizeToShow.X < SizeShown.X)) { /* * This new font is smaller than nFont */ DBGFONTS(("INSERT at %x, SizeToShow = (%d,%d)\n", nFont, SizeToShow.X,SizeToShow.Y)); nFontNew = nFont; goto InsertNewFont; } } /* * The font we are adding should be appended to the list, * since it is bigger (or equal) to the last one. */ nFontNew = (LONG)NumberOfFonts; InsertNewFont: // at nFontNew // ASSERT ((lpTextMetric->tmPitchAndFamily & 1) == 0); /* If we have to grow our font table, do it */ if (NumberOfFonts == FontInfoLength) { PFONT_INFO Temp; FontInfoLength += FONT_INCREMENT; Temp = (PFONT_INFO)HeapReAlloc(pConHeap,MAKE_TAG( FONT_TAG ),FontInfo,sizeof(FONT_INFO) * FontInfoLength); ASSERT(Temp); if (Temp == NULL) { FontInfoLength -= FONT_INCREMENT; return FALSE; } FontInfo = Temp; } if (nFontNew < (LONG)NumberOfFonts) { RtlMoveMemory(&FontInfo[nFontNew+1], &FontInfo[nFontNew], sizeof(FONT_INFO)*(NumberOfFonts - nFontNew)); } /* * Store the font info */ FontInfo[nFontNew].hFont = hFont; FontInfo[nFontNew].Family = tmFamily; FontInfo[nFontNew].Size = SizeActual; if (TM_IS_TT_FONT(tmFamily)) { FontInfo[nFontNew].SizeWant = SizeWant; } else { FontInfo[nFontNew].SizeWant.X = 0; FontInfo[nFontNew].SizeWant.Y = 0; } FontInfo[nFontNew].Weight = tmi.tmWeight; FontInfo[nFont].FaceName = pFN->awch; ++NumberOfFonts; if (nFontType == TRUETYPE_FONTTYPE && !IS_BOLD(FontInfo[nFontNew].Weight)) { lpLogFont->elfLogFont.lfWeight = FW_BOLD; goto CreateBoldFont; } pfed->ulFE |= FE_FONTOK; // and continue enumeration return TRUE; }
int CD3DFont::Init() { // Create vertex buffer for the letters HRESULT hr; // Prepare to create a bitmap unsigned int* pBitmapBits; BITMAPINFO bmi; ZeroMemory(&bmi.bmiHeader, sizeof(BITMAPINFOHEADER)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = (int)m_dwTexWidth; bmi.bmiHeader.biHeight = -(int)m_dwTexHeight; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biBitCount = 32; // Create a DC and a bitmap for the font HDC hDC = CreateCompatibleDC(nullptr); HBITMAP hbmBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&pBitmapBits, nullptr, 0); SetMapMode(hDC, MM_TEXT); // create a GDI font HFONT hFont = CreateFont(24, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH, _T("Tahoma")); if (nullptr == hFont) return E_FAIL; HGDIOBJ hOldbmBitmap = SelectObject(hDC, hbmBitmap); HGDIOBJ hOldFont = SelectObject(hDC, hFont); // Set text properties SetTextColor(hDC, 0xFFFFFF); SetBkColor(hDC, 0); SetTextAlign(hDC, TA_TOP); TEXTMETRICW tm; GetTextMetricsW(hDC, &tm); m_LineHeight = tm.tmHeight; // Loop through all printable characters and output them to the bitmap // Meanwhile, keep track of the corresponding tex coords for each character. int x = 0, y = 0; char str[2] = "\0"; for (int c = 0; c < 127 - 32; c++) { str[0] = c + 32; SIZE size; GetTextExtentPoint32A(hDC, str, 1, &size); if ((int)(x + size.cx + 1) > m_dwTexWidth) { x = 0; y += m_LineHeight; } ExtTextOutA(hDC, x + 1, y + 0, ETO_OPAQUE | ETO_CLIPPED, nullptr, str, 1, nullptr); m_fTexCoords[c][0] = ((float)(x + 0)) / m_dwTexWidth; m_fTexCoords[c][1] = ((float)(y + 0)) / m_dwTexHeight; m_fTexCoords[c][2] = ((float)(x + 0 + size.cx)) / m_dwTexWidth; m_fTexCoords[c][3] = ((float)(y + 0 + size.cy)) / m_dwTexHeight; x += size.cx + 3; // 3 to work around annoying ij conflict (part of the j ends up with the i) } // Create a new texture for the font // possible optimization: store the converted data in a buffer and fill the texture on creation. // That way, we can use a static texture ID3D11Texture2D* buftex; D3D11_TEXTURE2D_DESC texdesc = CD3D11_TEXTURE2D_DESC(D3D::GetBaseBufferFormat(), m_dwTexWidth, m_dwTexHeight, 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); hr = device->CreateTexture2D(&texdesc, nullptr, &buftex); if (FAILED(hr)) { PanicAlert("Failed to create font texture"); return hr; } D3D::SetDebugObjectName(buftex, "texture of a CD3DFont object"); // Lock the surface and write the alpha values for the set pixels D3D11_MAPPED_SUBRESOURCE texmap; hr = context->Map(buftex, 0, D3D11_MAP_WRITE_DISCARD, 0, &texmap); if (FAILED(hr)) PanicAlert("Failed to map a texture at %s %d\n", __FILE__, __LINE__); for (y = 0; y < m_dwTexHeight; y++) { u32* pDst32 = (u32*)((u8*)texmap.pData + y * texmap.RowPitch); for (x = 0; x < m_dwTexWidth; x++) { const u8 bAlpha = (pBitmapBits[m_dwTexWidth * y + x] & 0xff); *pDst32++ = (((bAlpha << 4) | bAlpha) << 24) | 0xFFFFFF; } } // Done updating texture, so clean up used objects context->Unmap(buftex, 0); hr = D3D::device->CreateShaderResourceView(buftex, nullptr, ToAddr(m_pTexture)); if (FAILED(hr)) PanicAlert("Failed to create shader resource view at %s %d\n", __FILE__, __LINE__); SAFE_RELEASE(buftex); SelectObject(hDC, hOldbmBitmap); DeleteObject(hbmBitmap); SelectObject(hDC, hOldFont); DeleteObject(hFont); // setup device objects for drawing m_pshader = D3D::CompileAndCreatePixelShader(fontpixshader); if (m_pshader == nullptr) PanicAlert("Failed to create pixel shader, %s %d\n", __FILE__, __LINE__); D3D::SetDebugObjectName(m_pshader.get(), "pixel shader of a CD3DFont object"); D3DBlob vsbytecode; D3D::CompileShader(DX11::D3D::ShaderType::Vertex, fontvertshader, vsbytecode); if (vsbytecode.Data() == nullptr) PanicAlert("Failed to compile vertex shader, %s %d\n", __FILE__, __LINE__); m_vshader = D3D::CreateVertexShaderFromByteCode(vsbytecode); if (m_vshader.get() == nullptr) PanicAlert("Failed to create vertex shader, %s %d\n", __FILE__, __LINE__); D3D::SetDebugObjectName(m_vshader.get(), "vertex shader of a CD3DFont object"); const D3D11_INPUT_ELEMENT_DESC desc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; hr = D3D::device->CreateInputLayout(desc, 3, vsbytecode.Data(), vsbytecode.Size(), ToAddr(m_InputLayout)); if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); D3D11_BLEND_DESC blenddesc; blenddesc.AlphaToCoverageEnable = FALSE; blenddesc.IndependentBlendEnable = FALSE; blenddesc.RenderTarget[0].BlendEnable = TRUE; blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; hr = D3D::device->CreateBlendState(&blenddesc, ToAddr(m_blendstate)); CHECK(hr == S_OK, "Create font blend state"); D3D::SetDebugObjectName(m_blendstate.get(), "blend state of a CD3DFont object"); D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0.f, false, false, false, false); hr = D3D::device->CreateRasterizerState(&rastdesc, ToAddr(m_raststate)); CHECK(hr == S_OK, "Create font rasterizer state"); D3D::SetDebugObjectName(m_raststate.get(), "rasterizer state of a CD3DFont object"); D3D11_BUFFER_DESC vbdesc = CD3D11_BUFFER_DESC(MAX_NUM_VERTICES*sizeof(FONT2DVERTEX), D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); if (FAILED(hr = device->CreateBuffer(&vbdesc, nullptr, ToAddr(m_pVB)))) { PanicAlert("Failed to create font vertex buffer at %s, line %d\n", __FILE__, __LINE__); return hr; } D3D::SetDebugObjectName(m_pVB.get(), "vertex buffer of a CD3DFont object"); return S_OK; }