How do I draw the raster fonts used by the Windows console in a GDI application? For instance, the 8x8 fixed font as shown in this screenshot.
Can these fonts be used via the CreateFont() API, or is there some special way that Windows loads these fonts?
The console uses "fixed width fonts", such as "Courier New" (available in all Windows version) or "Consolas" (available since Vista).
Fixed width fonts are not necessarily raster. To use raster fonts, enumerate fonts to find a raster font such as "Terminal" or "Fixedsys". You have to use the right size (example, 18 for "Terminal" font) otherwise Windows may substitute a different font and resize. There are also issues with DPI settings. If program is not DPI aware then magnification will occur if work station has high DPI settings.
case WM_PAINT:
{
PAINTSTRUCT ps;
auto hdc = BeginPaint(hwnd, &ps);
auto hfont = CreateFont(-18, 0, 0, 0, 0, 0, 0, 0,
ANSI_CHARSET,
OUT_DEVICE_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH,
L"Terminal");
auto oldfont = SelectObject(hdc, hfont);
RECT rc = { 0,0,100,300 };
DrawText(hdc, L"Test 123", -1, &rc, DT_LEFT | DT_TOP);
SelectObject(hdc, oldfont);
DeleteObject(hfont);
EndPaint(hwnd, &ps);
return 0;
}
The answer was similar to Barmak's answer, with the difference that both width and height are specified, so to create a font for the 8x8 raster font I use the following code:
hfont = CreateFont(-8, -8, 0, 0, 0, 0, 0, 0, OEM_CHARSET, OUT_DEVICE_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, _T("Terminal"));
Specifically, both the height and width must be specified, and the OEM_CHARSET charset must be specified, in order to select one of the Raster Fonts.
My intent is to render to a DirectDraw surface (IDirectDrawSurface7::GetDC()) then subsequently paint that surface to the primary, as shown here:
With a bit of trickery involving multiple passes I added a bit of a shadow effect to the text, however that is outside the scope of my original question.
Related
There are some WinAPI functions that draw TrueType fonts into a windows GDI context.
I want to have this TrueType text written, but into my own 2-dimensional array of pixels (the one I just blit onto a window with just this code :)
int blit_mode = STRETCH_DELETESCANS;
void BlitFrame()
{
BITMAPINFO bmi = { {sizeof(BITMAPINFOHEADER), frame_size_x, -frame_size_y,1,32,BI_RGB,0,0,0,0,0}, {0,0,0,0} };
SetStretchBltMode(hdc, blit_mode);
int result = StretchDIBits(hdc,
0, 0, client_x, client_y,
0, 0, frame_size_x, frame_size_y,
frame_bitmap,
&bmi,
DIB_RGB_COLORS,
SRCCOPY);
}
This code is very messy in WinAPI and I couldn't find out (at least to this moment) how to do that.
How can I do that?
tnx Barmak Shemirani
i get your code and produced something like that
void BlitFrame2()
{
BITMAPINFO bmi = { {sizeof(BITMAPINFOHEADER), frame_size_x, -frame_size_y,1,32,BI_RGB,0,0,0,0,0}, {0,0,0,0} };
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateBitmap(frame_size_x, frame_size_y, 1, 32, frame_bitmap);
HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
SetBkMode(memdc, TRANSPARENT);
SetTextColor(memdc, 0xffffff);
TextOut(memdc, 0, 0, "123", 3);
SelectObject(memdc, oldbmp);
GetDIBits(memdc, hbitmap, 0, frame_size_y, frame_bitmap, &bmi, 0);
DeleteObject(hbitmap);
DeleteDC(memdc);
// ReleaseDC(0, hdc);
SetStretchBltMode(hdc, blit_mode);
int result = StretchDIBits(hdc,
0, 0, client_x, client_y,
0, 0, frame_size_x, frame_size_y,
frame_bitmap,
&bmi,
DIB_RGB_COLORS,
SRCCOPY);
}
its adds stable text to my blitted frames, tnx
hovever i would get yet some question as i dont understand it
1) could maybe someone tell me a bit more how it works and where memory transfers are? do i have simple byte acces to this pixel table that is updated with drawed text? (for example to postprocess it)
2) it works but it gets slower quite noticably, for example when my oryginal frame was 2 ms (draw some bitmap sprite then blit) when using this tame grows up to 8 ms
3) can i move some of those calls outside of frame loop?
PS
when thinking on this i assume it works like that
1) it copies my pixel table in memdc at some point (where?)
2) it draws to this memdc those fonts
3) GetDIBits updates my oryginal pixel table with changed pixels (im not sure as to this hovever, but almost sure)
4) i blit it just like before
if so instead of one blit i get three (co it should be 3 times slower,
measurements show its more like 4 times, but maybe its an measurment error (2 and 8 may be 2.7 and 8.1 for example)
if this is three it would be ok, hovever i think i not always would need to get those table pixels reupdated from memdc, is there a way of
blitting it stright from memdc? (then it would be only two times slower instead of 3, still it is sad those fount routines cant just render stright into my own ram table - then it would be no slower at all) (isnt it really possible?)
I am writing a simple program, that prints some text on the screen, overlaying the other windows.
#include "stdafx.h"
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
HWND hwnd = GetDesktopWindow();
HDC hdc;
RECT rect;
//LPRECT rect = new RECT;
wchar_t text[] = L"test";
GetClientRect(hwnd, &rect);
do{
hdc = GetWindowDC(hwnd);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(100, 100, 100));
DrawText(hdc, text, -1, &rect, DT_NOCLIP);
ReleaseDC(hwnd, hdc);
Sleep(15);
} while (1);
return 0;
}
The problem is that I would like the background of the printed text to be transparent, but SetBkMode does not seem to work (it actually makes no difference if I set it to OPAQUE or TRANSPARENT) so I get a solid background. Any ideas? What am I missing?
edit: Changed LPRECT to RECT, as suggested.
edit: using transparent window:
creating window:
CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED, // extended style
(LPCWSTR)WINDOW_CLASS_NAME, // class
L"test", // title
NULL,
0, 0, // initial x,y
400, 300, // initial width, height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL)
globals:
wchar_t tst_Str[] = L"TEST";
WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
SetTextColor(hdc, RGB(255, 0, 0));
DrawText(hdc, tst_Str, -1, &rc,NULL);
EndPaint(hwnd, &ps);
return 0;
Windows are responsible for painting themselves. The system is not designed to allow external parties to draw on other windows in the manner you are attempting. You will have to recalibrate your expectations.
If you wish to draw something you should create a window and draw on it. You are at liberty to make the window a transparent layered window and position your drawing so that it appears on top of another window. And you also gain the benefit that you don't need to run a busy loop. You can paint in the standard way, in response to WM_PAINT messages.
To make a layered window appear on the desktop, you have to call SetLayeredWindowAttributes function, for example
SetLayeredWindowAttributes(hWnd,
RGB(255, 255, 255), // The transparent color
128, // Opacity of the window
LWA_ALPHA | LWA_COLORKEY); // Enable both features
This example will make portions of the window with white color completely transparent. Parameter bAlpha specifies the opacity of the window (the pixels not affected with crKey).
Here's a screen shot of such window with Test text floating over browser window with this question:
If you create your window without border, using these window styles: WS_POPUP | WS_SYSMENU, then it will look like on the screen shot. I included WS_SYSMENU to have the standard window menu in the taskbar. To force the taskbar to display a button for popup window, include WS_EX_APPWINDOW extended style.
I'm studying Drawing Shapes by WinAPI C++
I tried to draw 2 ellipse with some codes on WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HPEN hPen = CreatePen(PS_DASHDOTDOT, 2, NULL);SelectObject(hdc, hPen);
Ellipse(hdc, 100, 200, 400, 400);
Ellipse(hdc, 300, 300, 500, 510);
DeleteObject(hPen);
EndPaint(hWnd, &ps);
But the output is:
The result I expect is neither shapes is front of the other. And the border is dash dot dot.
Can anyone show me my mistake? I appreciate for your help.
Ellipse() (like all GDI shape functions) fills the shape using the current brush, which is why your output looks like that. For details on that see setting pen and brush colors.
If you want just the ellipse with no fill, first select a null brush:
SelectObject( hdc, GetStockObject( NULL_BRUSH ) );
One appears on top of the other because you're not just drawing the outline, but filling it as well. To stop filling it, you can select a "hollow brush", sometimes called a "null brush".
HBRUSH hbrOld = SelectObject(hdc, GetStockObject(HOLLOW_BRUSH));
// draw your ellipses here
You can only create dotted or dashed pens with widths of 1 or 0. You used 2, so the command failed.
Also, you should select the pen back out of the DC before you delete it.
I need to make bird animation in WS_OVERLAPPED window (as shown below). Animation is represented by 8 images:
The blue color in the image (which is RGB(0, 255, 255)) has to be transparent (see screenshot below).
I wanted to do this using CreateWindowEx() (bird would be represented by layered window) with WS_EX_LAYERED argument. Unfortunately bird must be WS_CHILD. Mixing WS_EX_LAYERED | WS_CHILD is not legal in Windows 7:
Windows 8: The WS_EX_LAYERED style is supported for top-level windows and child windows. Previous Windows versions support WS_EX_LAYERED only for top-level windows.
Final effect should look like this (I've already painted window's background - the only problem is the bird):
How can I achieve this effect? How to animate bird in parent window?
If you have any idea how to implement bird animation with transparent background color please share.
Since animation is done even when there's no interaction with the window, we'll need a timer:
case WM_CREATE:
// load resources
SetTimer(hwnd, 0, 250, NULL); // set timer to 250 ms
return 0;
...
case WM_DESTROY:
KillTimer(hwnd, 0);
// release the resources
return 0;
We can invalidate the whole window each timer tick, but it would be better to redraw only needed part. We'll also update the current frame number here:
case WM_TIMER:
frame_number++;
if (frame_number >= 8)
frame_number = 0;
RECT rc = { 30, 30, 80, 80 }; // a rectangle from (30,30) to (80,80)
InvalidateRect(hwnd, &rc, FALSE);
return 0;
Then, we draw the current frame in the WM_PAINT handler:
case WM_PAINT:
// draw the sky
SelectObject(hDCMem, hBird);
TransparentBlt(hDC, 30, 30, 50, 50, hDCMem, frame_number * 51, 0, 50, 50, RGB(0, 255, 255)); // 51 is 50 (side of a bird frame) + 1 (gap between the frames)
// draw the rest
return 0;
I've finally find out how to do it. It's quite tricky.
The full description of the solution is available here - winprog.org/tutorial/transparency.html. For polish readers here is great translation.
Simple idea in brief:
Giving bitmaps the appearance of having transparent sections is quite simple, and involves the use of a black and white Mask image in addition to the colour image that we want to look transparent.
The following conditions need to be met for the effect to work correctly: First off, the colour image must be black in all areas that we want to display as transparent. And second, the mask image must be white in the areas we want transparent, and black elsewhere. The colour and mask images are displayed as the two left most images in the example picture on this page.
Simple solution in brief:
#define TRANSPARENCY_COLOR RGB(0, 255, 255)
birdBmp = (HBITMAP) LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
hbmpMask = CreateBitmapMask(birdBmp, TRANSPARENCY_COLOR);
Painting:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
HDC birdMaskHdc = CreateCompatibleDC(hdc);
BITMAP bmInfo;
GetObject(birdBmp, sizeof(bmInfo), &bmInfo);
HBITMAP hbmpOld = (HBITMAP) SelectObject(birdMaskHdc, hbmpMask);
BitBlt(hdc, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, birdMaskHdc, 0, 0, SRCAND);
SelectObject(birdMaskHdc, birdBmp);
BitBlt(hdc, 0, 0, bmInfo.bmWidth, bmInfo.bmHeight, birdMaskHdc, 0, 0, SRCPAINT);
SelectObject(birdMaskHdc, hbmpOld);
DeleteDC(birdMaskHdc);
EndPaint(hWnd, &ps);
break;
}
Cleaning:
case WM_DESTROY:
{
DeleteObject(hbmpMask);
DeleteObject(birdBmp);
PostQuitMessage(0);
break;
}
Function which is responsible for creating bitmap mask:
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC hdcMem, hdcMem2;
HBITMAP hbmMask, hbmOld, hbmOld2;
BITMAP bm;
GetObject( hbmColour, sizeof( BITMAP ), & bm );
hbmMask = CreateBitmap( bm.bmWidth, bm.bmHeight, 1, 1, NULL );
hdcMem = CreateCompatibleDC( NULL );
hdcMem2 = CreateCompatibleDC( NULL );
hbmOld =( HBITMAP ) SelectObject( hdcMem, hbmColour );
hbmOld2 =( HBITMAP ) SelectObject( hdcMem2, hbmMask );
SetBkColor( hdcMem, crTransparent );
BitBlt( hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY );
BitBlt( hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT );
SelectObject( hdcMem, hbmOld );
SelectObject( hdcMem2, hbmOld2 );
DeleteDC( hdcMem );
DeleteDC( hdcMem2 );
return hbmMask;
}
I am running this following code,
HDC hdc;
HDC hdcMem;
HBITMAP bitmap;
RECT c;
GetClientRect(viewHandle, &c);
// instead of BeginPaint use GetDC or GetWindowDC
hdc = GetDC(viewHandle);
hdcMem = CreateCompatibleDC(hdc);
// always create the bitmap for the memdc from the window dc
bitmap = CreateCompatibleBitmap(hdc,c.right-c.left,200);
SelectObject(hdcMem, bitmap);
// only execute the code up to this point one time
// that is, you only need to create the back buffer once
// you can reuse it over and over again after that
// draw on hdcMem
// for example ...
Rectangle(hdcMem, 126, 0, 624, 400);
// when finished drawing blit the hdcMem to the hdc
BitBlt(hdc, 0, 0, c.right-c.left,200, hdcMem, 0, 0, SRCCOPY);
// note, height is not spelled i before e
// Clean up - only need to do this one time as well
DeleteDC(hdcMem);
DeleteObject(bitmap);
ReleaseDC(viewHandle, hdc);
The code is just fine. But I am seeing black color around this rectangle. Why is that? Here is an example image.
The bitmap is most likely initialized to be all black. You are then drawing a white rectangle that between x-coordinates 126 and 624. Hence, everything to the left of x=126 and to the right of x=624 stays black.
Edit: The documentation for CreateCompatibleBitmap doesn't state how the bitmap will be initialized, so you should explicitly initialize the bitmap with a specific colour, as Goz suggests, using FillRect:
RECT rc;
rc.left=0;
rc.top=0;
rc.right=c.right-c.left;
rc.bottom=200;
FillRect(hdcMem, &rc, (HBRUSH)GetStockObject(GRAY_BRUSH));
This example fills the bitmap in gray -- you may need to CreateSolidBrush your own brush if you need a different colour. (Don't forget to call DeleteObject when you're done.)
As a side note, I find it a bit strange that your bitmap is being set to a constant height of 200 -- the normal thing would be to make the height of the bitmap equal to the height of the window (as is done for the width).
Might it be because you haven't initialised the memory bitmap area to a given colour? Try FillRect'ing the background to a different colour then draw your white rectangle over it and see what happens.
Per MSDN http://msdn.microsoft.com/en-us/library/dd162898.aspx:
The rectangle is outlined by using the current pen and filled by using the current brush.
Consider calling FillRect instead, or select an appropriate pen prior to calling Rectangle'.
I used:
// Fill the background
hdcMem->FillSolidRect(c, hdcMem->GetBkColor());
Just as a note.