So I'm trying to using CreateFont to try to create a font similar to the one I have in Photoshop, but if you look close the quality in Photoshop is a lot better.
Here's the code I'm using to create the font:
CreateFont(45, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_MODERN, __T("Times New Roman"));
And here is a comparison of the two fonts:
As you can see the top one is much smoother.
Should I be using different arguments in CreateFont? I'll admit I don't completely understand them all.
An explanation would be nice.
Photoshop uses its own antialiasing and applies it consistently.
Windows only uses antialiasing on font sizes within certain limits, and it looks like your sample is outside the limits. Here's a quote from the CreateFont documentation (emphasis added):
ANTIALIASED_QUALITY Font is antialiased, or smoothed, if the font supports it and the size of the font is not too small or too large.
You might have better control in GDI+.
Related
I'm currently working on developing an internal ui for games (just like imgui) and am stuck on the quality of fonts when they are rendered through DrawText (ID3DXFont), I've tried to use DrawTextA and link a sprite to the rendering of the font but it doesn't look like there is a difference. When comparing it to imgui it seems as if the quality of the text is much higher and is anti-aliased properly, although when using ANTIALIASED_QUALITY when creating the font doesnt seem much like the anti-aliasing done in imgui at all. I'd like to make my text look at good as imgui's as im really fed up with ID3DXFont looks but am wondering on how to make it look better.
Here's some examples between ID3DXFont and Imgui (both fonts are Arial Bold and rendered with size 23)
ImGui (in d3d9):
ID3DXFont:
this is what my text rendering looks like:
ID3DXSprite* Sprite = nullptr;
D3DXCreateSprite(Device, &Sprite);
Sprite->Begin(D3DXSPRITE_ALPHABLEND);
Font->DrawTextA(Sprite, Text.c_str( ), Text.length( ), &Size, DT_NOCLIP, D3DColor);
Sprite->End( );
Sprite->Release( );
and my font initialization:
D3DXCreateFont(Device, 23.f, 0, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, "Arial", &Font);
The first thing to say is that Direct3D 9's ID3DXFont interface is extremely old at this point, dating back to 2002 or even earlier. D3DX9, D3DX10, and D3DX11 are all deprecated as is the legacy DirectX SDK itself. See this blog post.
For Direct3D 10 or later, the recommended solution for high-quality, scalable font rendering on DirectX surfaces is DirectWrite (using Direct2D to render). The interop is a little complicated prior to DirectX 11.1, but is pretty simple with it (Windows 7 SP1 + KB2670838 installed, Windows 8.x, and Windows 10). See Microsoft Docs for DX11 / DX12.
The ID3DXFont solution as well as SpriteFont in the DirectX Tool Kit for DX11 / DX12 capture the TrueType glyphs into a texture at a specific font-size and then render them as a sprite-sheet. This is very fast, but does not have the scaling behavior of a vector-font solution like DirectWrite, FreeType, etc. That said, sprite-sheet fonts tend to look great as long as you are rendering them at the same resolution as they were captured at.
The images you show above are not 23 points high so they are going to squash a bit and you'll notice the difference between FreeType and a sprite-sheet solution in that case.
The D3XD9 ID3DXFont solution is a little more primitive than SpriteFont. D3DX9 always uses D3DFMT_A8R8G8B8 for the captured texture. SpriteFont can use a DXGI_FORMAT_R8G8B8A8, DXGI_FORMAT_B4G4R4A4, or DXGI_FORMAT_BC2_UNORM encoding. SpriteFont is also optimized for premultiplied alpha rendering.
My goal is to have a fullscreen overlaying invisible "canvas" on which I can draw using win32's various drawing functions.
The way I am currently attempting it is this:
WNDCLASSA myclass = { 0 };
myclass.lpfnWndProc = WindowProc3;
myclass.hInstance = GetModuleHandle(0);
myclass.lpszClassName = "MyCanvas";
myclass.hbrBackground = CreateSolidBrush(0xFEEDBEEF);
myclass.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassA(&myclass);
...
HWND wnd = CreateWindowExA(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "MyCanvas", 0, WS_POPUP | WS_VISIBLE, 0, 0, screen_width, screen_height, 0, 0, GetModuleHandle(0), 0);
SetLayeredWindowAttributes(wnd, 0xFEEDBEEF, 0, LWA_COLORKEY);
Although this serves as a canvas, hours of googling later, I am still unable to draw on it semitransparently.
I have added a screenshot of what my program is currently displaying as I am writing this. What I would like to be able to do is, for example, make the black box in the top right corner (drawn with Rectangle) semitransparent so as to reveal the stackoverflow page content below it.
This is a question I found that I was hopeful about, but the resulting text is just a blended combination of the background color ((COLORREF)0xFEEDBEEF) and text color. Other things I have found have either just made the element fully invisible, done nothing at all, or required some library like MFC. I want to only use win32 functions if at all possible, as I would like to be able to achieve the highest FPS possible.
I do not care if this doesn't work on all Windows versions as long as it does on 7 up to 10.
If you only need transparency for a rectangular area where all pixels either have the same transparency (aka alpha) value or are completely transparent, you can use SetLayeredWindowAttributes() with a combination of alpha value and/or color key.
UpdateLayeredWindow() is the way to go if you need to be able to define transparency per-pixel.
For that you have to create memory DC and select a 32bpp bitmap into it. You may use the buffered paint API to ease the task. Raymond Chen has a blog post with a code sample about that.
You can draw into the memory DC, but you can't use most of GDI API for that, because GDI ignores the alpha channel (transparency). I suggest using GDI+ which allows you to specify the alpha values.
After you have completed drawing into the memory DC, you would call UpdateLayeredWindow() and pass that memory DC as the argument for the hdcSrc parameter to make the result visible on screen.
Illustration of possible effects:
SetLayeredWindowAttributes( hwnd, 0, 176, LWA_ALPHA );
SetLayeredWindowAttributes( hwnd, colorkey, 0, LWA_COLORKEY );
SetLayeredWindowAttributes( hwnd, colorkey, 176, LWA_ALPHA|LWA_COLORKEY );
UpdateLayeredWindow( ... )
Note the antialiased edge of the shape and the transparency gradient in the last example. Things like that are only possible with UpdateLayeredWindow().
I need a way to display a PNG image with transparent background on a background with gradient color.
I have tried this:
CImage img;
CBitmap bmp;
img.Load(_T(".\\res\\foo.png"));
bmp.Attach(img.Detach());
CDC dcStatus;
dcStatus.CreateCompatibleDC(&dc);
dcStatus.SelectObject(&bmp);
dcStatus.SetBkColor(TRANSPARENT);
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcStatus, 0, 0, SRCCOPY);
bmp.DeleteObject();
but foo.png gets black background wherever it is transparent in original image.
I did try to do a new bitmap that was painted with transparent color and did all possible operations on it, but that didn't help. Sample of one permutation:
CImage img;
CBitmap bmp;
img.Load(_T(".\\res\\foo.png"));
bmp.Attach(img.Detach());
CBitmap bmpMaska;
bmpMaska.CreateBitmap(14, 14, 1, 1, NULL);
CDC dcStatus;
dcStatus.CreateCompatibleDC(&dc);
dcStatus.SelectObject(&bmp);
CDC dcMaska;
dcMaska.CreateCompatibleDC(&dc);
dcMaska.SelectObject(&bmpMaska);
dcMaska.SetBkColor(dcStatus.GetPixel(0, 0));
//TODO: Bitmap ni transparent
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcMaska, 0, 0, SRCCOPY);
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcStatus, 0, 0, SRCAND);
bmp.DeleteObject();
bmpMaska.DeleteObject();
This did not do the trick. Either, there was all black square on the screen, or the result was the same as original.
I have also checked AlphaBlend API, but my code must be pure MFC + C++ witthout any additional APIs.
[Edit]: Company policy is as little APIs as possible. The code is supposed to run on embedded windows systems in real time.
[Edit 2]: I am not bound to PNG image format, meaning, anything that will display as transparent, goes.
Please, tell me what am I doing wrong?
Convert the png to a 32 bit bmp file. Google 'AlphaConv' and use that. PNG's are a PITA to work with - several API's claim to support them but actually don't, or only partially, or only on some platforms etc.
Load your 32-bit bmp using CBitmap::LoadBitmap (from resources - this is by far the easiest)
Then use CDC::AlphaBlend() to draw your bitmap. TransparentBlt() doesn't work with an alpha channel - it's just either draw the pixel or not. AlphaBlend is part of the win32 API, and you could still investigate CImage::AlphaBlend if you'd like, but that has a bug somewhere (I forgot what exactly) so I always use the raw ::AlphaBlend.
But beware - you need to premultipy the alpha channel for correct display. See my answer on How to draw 32-bit alpha channel bitmaps? .
No variation of what you described will work. You need another API to get this to work. Or, you could use GetDIBits and do your own version of ::AlphaBlend() :)
Based on what you're asking for (simply some set of pixels being transparent) it's probably easiest to use a 24- or 32-bit BMP, and simply pick a color to treat as transparent. Pre-process your picture to take any pixel that precisely matches your transparent color and increase change the least significant bit of one of the channels.
Given 8 bits per channel, this won't normally cause a visible change. Then draw the pixels you want transparent in one exact color. This is easy to do in something like a paint program.
You can then draw that to your DC using TransparentBlt. That lets you specify the color you want to treat as transparent.
I'm displaying a bitmap using GDI+. After loading the bitmap from a DLL resource I set the background colour (blue - #0000FF) to transparent using TransparentBlt. On Windows Vista and later this works as expected.
However, on a Windows XP system we're testing on this only works when any tooltip (e.g. the "title" property in IE, or Windows Explorer's tooltip shown when hovering the mouse over a file, etc) is displayed. The rest of the time the background colour is still blue.
Has anyone encountered this before, or know of a way to stop this occurring and for the blue to be properly made transparent?
Edit: After further investigation I found that setting colour depth in Windows XP to 16 bit colours instead of 32 bit colours caused TransparentBlt to start working normally again. Obviously this isn't an ideal solution, specifying what colour depth must be used, but does this give any hint to what might be happening?
Edit2: Code sample included.
m_pGDIBitmap = new Gdiplus::Bitmap(_Module.m_hInst, MAKEINTRESOURCE(lImageResource));
m_hMemDC = CreateCompatibleDC(hdc);
Gdiplus::Graphics myGraphics(m_hMemDC);
myGraphics.DrawImage(m_pGDIBitmap,
Gdiplus::Rect(0, 0, m_pGDIBitmap->GetWidth(), m_pGDIBitmap->GetHeight()),
0,
0,
m_pGDIBitmap->GetWidth(),
m_pGDIBitmap->GetHeight(),
Gdiplus::UnitPixel, &imAtt);
SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, rcBounds.left, rcBounds.top, NULL);
TransparentBlt(hdc, rcBounds.left, rcBounds.top, iScaledWidth, iScaledHeight, m_hMemDC, 0, 0, iBitmapWidth, iBitmapHeight, GetPixel(m_hMemDC, 0, 0));
You must show some sample code - the code that loads the bitmap and the code that blits it to display.
From the symptoms that you describe, my guess is that you load the bitmap not in its native format, but in the current display format. This means, that when the bit depth of the bitmap differs from the bit depth of the display, an automatic color space conversion is made. When this happens, the color that you provide to TransparentBlt is actually different from the color in the bitmap.
Possible alternative solutions:
Make sure that the bitmap is loaded in its native format without conversion.
Allow the conversion to take place, but instead of providing a hardcoded color value to TransparentBlt, make a GetPixel of a known "transparent" pixel of the bitmap (like top-left), and provide this value to the TransparentBlt.
What I ended up doing was more of a workaround, but it did work. I changed the background colour to black and added the following code before the DrawImage call:
Gdiplus::ImageAttributes imAtt;
imAtt.SetColorKey(Gdiplus::Color(0, 0, 0), Gdiplus::Color(0, 0, 0), Gdiplus::ColorAdjustTypeBitmap);
For some reason using this with blue as the background didn't work and using TransparentBlt on its own with either colour didn't work, but the combination applied the transparency correctly on the various OSes and colour depths that I tried.
If I had access to a paint program that supported bitmap files with an alpha channel, i.e. 32 bit bitmaps, I suspect making the background explicitly transparent and then using AlphaBlend would have worked as well but I didn't have the ability to try that at the time.
I'm trying to draw vertical text in win32 GDI api.
I call CreateFont() with 2700 for the angle and 900 for bold.
logPxlY = ::GetDeviceCaps (c->_hdc, LOGPIXELSY);
_hfont = ::CreateFont (-::MulDiv (point, logPxlY, 72),
0, angle, weight, 0, FALSE, FALSE, FALSE, 0, 0, 0, 0,
FIXED_PITCH | FF_MODERN, face);
where face is "Courier New", point is 9, angle is 2700 and weight is 900.
The text IS rotated correctly, but it's pretty dang faint compared to normal Courier New rendering. Is this since truetype is polishing the normal text real nice and isn't bothering with wierd rotated text or something?
Thanks for any insight :)
The font mapper can be a bit strange at times. 700 is bold, 900 is supposed to be "black". However, you probably have a Courier New Bold font file, so it can be used directly, whereas you probably do not have a Courier New Black font file, so it'll be synthesized -- probably from the normal Courier New font instead of from Courier New Bold (not sure why that is, but such is life).
If you try running through the possibilities, something like this (MFC code, for the moment, but the idea applies regardless):
CFont fonts[num];
pDC->SetBkMode(TRANSPARENT);
for (int i=0; i<num; i++) {
fonts[i].CreateFont(10, 0, 2700, 0, i*100, 0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, OUT_CHARACTER_PRECIS, PROOF_QUALITY, FF_MODERN, L"Courier New");
pDC->SelectObject(fonts+i);
pDC->TextOut(20+i*14, 20, L"This is some text");
}
What you'll probably see (what I get anyway) looks like this:
The 700 weight font is noticeably bolder than the 800 or 900 weight. This is not unique to Courier New either -- for example, the same code with the font name changed to Times New Roman produces output like this:
That's a bit closer, but the 700 weight still clearly looks darker than the 800 or 900. I won't bore you with screen shots, but the same effect happens when the text is horizontal.
Edit: One other minor detail: though it probably doesn't make any difference, you're currently computing the size of the font a bit incorrectly -- you're using logpixelsy, which makes sense when the font is horizontal. For a vertical font, you should use logpixelsx instead. On a typical screen, those will be identical, but on a printer (for example) they may be substantially different.