DrawText and TextOut are sensitive to memory aligment when drawing to a DIBSection

Louis Solomon / SteelBytes
Written 27/Aug/08

DrawTextW on 2000 and XP will not render to a DIBSection if the string is not WCHAR aligned in memory. But it does work in Vista.
TextOutW will not render to a DIBSection when the string is non WCHAR aligned on any of 2000, XP or Vista.
Both DrawTextW and TextOutW work fine when drawing to either a screen DC or a printer DC on all three Windows flavours.

In the following screen shots of Vista (x64 SP1) and XP (RTM), the first 4 lines are drawing to a DIBSection, and the 2nd 4 lines are drawing to the DC returned from BeginPaint

Source code to demonstrate

#define STRICT
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>

#if !defined(UNICODE)
#error this test doesn't make sense in Ansi
#endif

#define test_width 250
#define test_height 100

static void DrawStuff(HDC hdc,const RECT &rect)
{
	int width = rect.right-rect.left;
	int height = rect.bottom-rect.top;
	HFONT oldfont = SelectFont(hdc,CreateFont(height*1/4,0,0,0,0,0,0,0,0,0,0,0,0,TEXT("Tahoma")));
	SetTextColor(hdc,RGB(0,0,0));
	SetTextAlign(hdc,TA_LEFT|TA_TOP|TA_NOUPDATECP);
	SetBkMode(hdc,TRANSPARENT);
	{
		RECT r = {rect.left+0,rect.top+0,rect.left+width,rect.top+height};
		FillRect(hdc,&r,(HBRUSH)GetStockObject(WHITE_BRUSH));
	}
	{
		static const TCHAR string[] = TEXT("DrawText Aligned");
		RECT r = {rect.left+0,rect.top+height*0/4,rect.left+width,rect.top+height*1/4};
		DrawText(hdc,string,lstrlen(string),&r,DT_LEFT|DT_TOP|DT_NOPREFIX|DT_SINGLELINE);
	}
	{
		static const TCHAR string[] = TEXT("TextOut Aligned");
		TextOut(hdc,rect.left,rect.top+height*1/4,string,lstrlen(string));
	}
	{
		static const TCHAR _string[] = TEXT("DrawText Unaligned");
		BYTE buf[1+sizeof(_string)];
		CopyMemory(buf+1,_string,sizeof(_string));
		const TCHAR *string = (const TCHAR*)(buf+1);
		RECT r = {rect.left+0,rect.top+height*2/4,rect.left+width,rect.top+height*3/4};
		DrawText(hdc,string,lstrlen(string),&r,DT_LEFT|DT_TOP|DT_NOPREFIX|DT_SINGLELINE);
	}
	{
		static const TCHAR _string[] = TEXT("TextOut Unaligned");
		BYTE buf[1+sizeof(_string)];
		CopyMemory(buf+1,_string,sizeof(_string));
		const TCHAR *string = (const TCHAR*)(buf+1);
		TextOut(hdc,rect.left,rect.top+height*3/4,string,lstrlen(string));
	}
	DeleteObject(SelectFont(hdc,oldfont));
}

static LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
	switch (msg)
	{
	case WM_CREATE:
		return FALSE;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			BeginPaint(hwnd,&ps);
			{
				HDC pic_hdc = CreateCompatibleDC(NULL);
				BITMAPINFO bmInfo;
				ZeroMemory(&bmInfo,sizeof(bmInfo));
				bmInfo.bmiHeader.biSize = sizeof(bmInfo.bmiHeader);
				bmInfo.bmiHeader.biPlanes = 1;
				bmInfo.bmiHeader.biWidth = test_width;
				bmInfo.bmiHeader.biHeight = test_height;
				bmInfo.bmiHeader.biBitCount = 24;
				void *pic_buf;
				HBITMAP oldbmp = SelectBitmap(pic_hdc,CreateDIBSection(NULL,&bmInfo,DIB_RGB_COLORS,&pic_buf,NULL,NULL));
				RECT r = {0,0,test_width,test_height};
				DrawStuff(pic_hdc,r);
				BitBlt(ps.hdc,0,0,test_width,test_height,pic_hdc,0,0,SRCCOPY);
				DeleteObject(SelectBitmap(pic_hdc,oldbmp));
				DeleteDC(pic_hdc);
			}
			{
				RECT r = {0,test_height,test_width,test_height*2};
				DrawStuff(ps.hdc,r);
			}
			EndPaint(hwnd,&ps);
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		return FALSE;
	}
	return DefWindowProc(hwnd,msg,wParam,lParam);
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
	static const TCHAR szMainWindowClass[] = TEXT("MainWindowClass");
	WNDCLASS wc;
	ZeroMemory(&wc,sizeof(wc));
	wc.lpfnWndProc = MainWndProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);
	wc.lpszClassName = szMainWindowClass;
	RegisterClass(&wc);
	RECT r = {0,0,test_width,test_height*2};
	const DWORD style = WS_POPUPWINDOW|WS_CAPTION|WS_VISIBLE;
	const DWORD styleex = WS_EX_APPWINDOW|WS_EX_TOOLWINDOW;
	AdjustWindowRectEx(&r,style,FALSE,styleex);
	CreateWindowEx(styleex,szMainWindowClass,TEXT("DrawText mem align test"),style,100,100,r.right-r.left,r.bottom-r.top,NULL,NULL,hInstance,0);
	MSG msg;
	while (GetMessage(&msg,0,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}