Example #1
0
HBITMAP unscaledBitmap(void *i, intptr_t dx, intptr_t dy)
{
	BITMAPINFO bi;
	VOID *ppvBits;
	HBITMAP bitmap;

	ZeroMemory(&bi, sizeof (BITMAPINFO));
	bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bi.bmiHeader.biWidth = (LONG) dx;
	bi.bmiHeader.biHeight = -((LONG) dy);			// negative height to force top-down drawing;
	bi.bmiHeader.biPlanes = 1;
	bi.bmiHeader.biBitCount = 32;
	bi.bmiHeader.biCompression = BI_RGB;
	bi.bmiHeader.biSizeImage = (DWORD) (dx * dy * 4);
	bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &ppvBits, 0, 0);
	if (bitmap == NULL)
		xpanic("error creating HBITMAP for unscaled ImageList image copy", GetLastError());
	dotoARGB(i, (void *) ppvBits);
	return bitmap;
}
Example #2
0
static void paintArea(HWND hwnd, void *data)
{
	RECT xrect;
	PAINTSTRUCT ps;
	HDC hdc;
	HDC rdc;
	HBITMAP rbitmap, prevrbitmap;
	RECT rrect;
	BITMAPINFO bi;
	VOID *ppvBits;
	HBITMAP ibitmap;
	HDC idc;
	HBITMAP previbitmap;
	BLENDFUNCTION blendfunc;
	void *i;
	intptr_t dx, dy;
	int hscroll, vscroll;

	// FALSE here indicates don't send WM_ERASEBKGND
	if (GetUpdateRect(hwnd, &xrect, FALSE) == 0)
		return;		// no update rect; do nothing

	getScrollPos(hwnd, &hscroll, &vscroll);

	hdc = BeginPaint(hwnd, &ps);
	if (hdc == NULL)
		xpanic("error beginning Area repaint", GetLastError());

	// very big thanks to Ninjifox for suggesting this technique and helping me go through it

	// first let's create the destination image, which we fill with the windows background color
	// this is how we fake drawing the background; see also http://msdn.microsoft.com/en-us/library/ms969905.aspx
	rdc = CreateCompatibleDC(hdc);
	if (rdc == NULL)
		xpanic("error creating off-screen rendering DC", GetLastError());
	// the bitmap has to be compatible with the window
	// if we create a bitmap compatible with the DC we just created, it'll be monochrome
	// thanks to David Heffernan in http://stackoverflow.com/questions/23033636/winapi-gdi-fillrectcolor-btnface-fills-with-strange-grid-like-brush-on-window
	rbitmap = CreateCompatibleBitmap(hdc, xrect.right - xrect.left, xrect.bottom - xrect.top);
	if (rbitmap == NULL)
		xpanic("error creating off-screen rendering bitmap", GetLastError());
	prevrbitmap = (HBITMAP) SelectObject(rdc, rbitmap);
	if (prevrbitmap == NULL)
		xpanic("error connecting off-screen rendering bitmap to off-screen rendering DC", GetLastError());
	rrect.left = 0;
	rrect.right = xrect.right - xrect.left;
	rrect.top = 0;
	rrect.bottom = xrect.bottom - xrect.top;
	if (FillRect(rdc, &rrect, areaBackgroundBrush) == 0)
		xpanic("error filling off-screen rendering bitmap with the system background color", GetLastError());

	i = doPaint(&xrect, hscroll, vscroll, data, &dx, &dy);
	if (i == NULL)			// cliprect empty
		goto nobitmap;		// we need to blit the background no matter what

	// now we need to shove realbits into a bitmap
	// technically bitmaps don't know about alpha; they just ignore the alpha byte
	// AlphaBlend(), however, sees it - see http://msdn.microsoft.com/en-us/library/windows/desktop/dd183352%28v=vs.85%29.aspx
	ZeroMemory(&bi, sizeof (BITMAPINFO));
	bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
	bi.bmiHeader.biWidth = (LONG) dx;
	bi.bmiHeader.biHeight = -((LONG) dy);			// negative height to force top-down drawing
	bi.bmiHeader.biPlanes = 1;
	bi.bmiHeader.biBitCount = 32;
	bi.bmiHeader.biCompression = BI_RGB;
	bi.bmiHeader.biSizeImage = (DWORD) (dx * dy * 4);
	// this is all we need, but because this confused me at first, I will say the two pixels-per-meter fields are unused (see http://blogs.msdn.com/b/oldnewthing/archive/2013/05/15/10418646.aspx and page 581 of Charles Petzold's Programming Windows, Fifth Edition)
	// now for the trouble: CreateDIBSection() allocates the memory for us...
	ibitmap = CreateDIBSection(NULL,		// Ninjifox does this, so do some wine tests (http://source.winehq.org/source/dlls/gdi32/tests/bitmap.c#L725, thanks vpovirk in irc.freenode.net/#winehackers) and even Raymond Chen (http://blogs.msdn.com/b/oldnewthing/archive/2006/11/16/1086835.aspx), so.
		&bi, DIB_RGB_COLORS, &ppvBits, 0, 0);
	if (ibitmap == NULL)
		xpanic("error creating HBITMAP for image returned by AreaHandler.Paint()", GetLastError());

	// now we have to do TWO MORE things before we can finally do alpha blending
	// first, we need to load the bitmap memory, because Windows makes it for us
	// the pixels are arranged in RGBA order, but GDI requires BGRA
	// this turns out to be just ARGB in little endian; let's convert into this memory
	dotoARGB(i, (void *) ppvBits, FALSE);		// FALSE = not NRGBA

	// the second thing is... make a device context for the bitmap :|
	// Ninjifox just makes another compatible DC; we'll do the same
	idc = CreateCompatibleDC(hdc);
	if (idc == NULL)
		xpanic("error creating HDC for image returned by AreaHandler.Paint()", GetLastError());
	previbitmap = (HBITMAP) SelectObject(idc, ibitmap);
	if (previbitmap == NULL)
		xpanic("error connecting HBITMAP for image returned by AreaHandler.Paint() to its HDC", GetLastError());

	// AND FINALLY WE CAN DO THE ALPHA BLENDING!!!!!!111
	blendfunc.BlendOp = AC_SRC_OVER;
	blendfunc.BlendFlags = 0;
	blendfunc.SourceConstantAlpha = 255;		// only use per-pixel alphas
	blendfunc.AlphaFormat = AC_SRC_ALPHA;	// premultiplied
	if (AlphaBlend(rdc, 0, 0, (int) dx, (int) dy,		// destination
		idc, 0, 0, (int) dx, (int)dy,				// source
		blendfunc) == FALSE)
		xpanic("error alpha-blending image returned by AreaHandler.Paint() onto background", GetLastError());

	// clean up after idc/ibitmap here because of the goto nobitmap
	if (SelectObject(idc, previbitmap) != ibitmap)
		xpanic("error reverting HDC for image returned by AreaHandler.Paint() to original HBITMAP", GetLastError());
	if (DeleteObject(ibitmap) == 0)
		xpanic("error deleting HBITMAP for image returned by AreaHandler.Paint()", GetLastError());
	if (DeleteDC(idc) == 0)
		xpanic("error deleting HDC for image returned by AreaHandler.Paint()", GetLastError());

nobitmap:
	// and finally we can just blit that into the window
	if (BitBlt(hdc, xrect.left, xrect.top, xrect.right - xrect.left, xrect.bottom - xrect.top,
		rdc, 0, 0,			// from the rdc's origin
		SRCCOPY) == 0)
		xpanic("error blitting Area image to Area", GetLastError());

	// now to clean up
	if (SelectObject(rdc, prevrbitmap) != rbitmap)
		xpanic("error reverting HDC for off-screen rendering to original HBITMAP", GetLastError());
	if (DeleteObject(rbitmap) == 0)
		xpanic("error deleting HBITMAP for off-screen rendering", GetLastError());
	if (DeleteDC(rdc) == 0)
		xpanic("error deleting HDC for off-screen rendering", GetLastError());

	EndPaint(hwnd, &ps);
}