Technical: QuickTime
Advanced Search
Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

Mixing Quickdraw and Win32 Drawing: GWorlds, HDCs, HBITMAPs, and DIB sections

Dispatch 16

To ease integration, the Win32 implementation of Quickdraw incorporates Win32 graphics elements. When possible resources are allocated using native Win32 elements. If these resources are deemed valuable to the programmer we have provided accessor functions for their retrieval.

By default Quickdraw allocates GWorlds (offscreen drawing environments) by creating DIB sections. The DIB's memory (where the actual pixel values are stored) is shared by the GWorld. The GWorld's PixMap base address points to the location of the HBITMAP's pixel values. In this way both Quickdraw and Win32 can draw into the same graphic environment. For ease of use an HDC is created and the HBITMAP is selected into it for the life of the GWorld. Changes in the GWorld drawing environment are reflected in the corresponding HDC and HBITMAP. If Quickdraw is unable to allocate the GWorld as a DIB section, it falls back to allocating the offscreen drawing pixels in memory.

You can access a GWorld's HDC and HBITMAP by using QuickTime's GetPortHDC and GetPortHBITMAP functions. Your application must not dispose of the HBITMAP or HDC returned from these calls. They are owned by QuickTime and will automatically be disposed when the GWorld is disposed.

You may also request that the offscreen GWorld is allocated as a DirectDraw surface by passing the kAllocDirectDrawSurface flag to the NewGWorld family of calls.

Rendering into an HBITMAP

A common question is how to use QuickTime or Quickdraw to render into an HBITMAP. Since most GWorlds are simply wrappers for HBITMAPs this is fairly straightforward.

The RenderIntoHBITMAPExample function below demonstrates the relationship of GWorlds and HBITMAPs as well as the use of QTNewGWorld, SetGWorld, GetPortHDC, and GetPortHBITMAP. The sample code first creates a GWorld in a Win32 compatible pixel format (in our example code we use k32BGRAPixelFormat). Once created the GWorld's HDC and HBITMAP are retrieved using GetPortHDC and GetPortHBITMAP. The active port is selected by calling SetGWorld, and graphics rendered using the Quickdraw APIs RGBForeColor and PaintRect. Additionally, native Win32 GDI calls are used to render graphics into the same GWorld. Finally, the contents of the port is copied to a secondary HDC via ScaleBlt, using the GWorld's HDC as a source. Note that the HBITMAP and HDC are owned and managed by the GWorld, and are not disposed of.

OSErr RenderIntoHBITMAPExample(HDC hdcDest, RECT *rectDest)
{
	GWorldPtr gw = nil;
	CGrafPtr savedPort;
	GDHandle savedGD;
	HDC hdcSrc;
	HBITMAP hbitmapSrc;
	Rect bounds;
	OSErr result = noErr;
   
	// Create a 256 x 256 32BGRA GWorld
	bounds.top = bounds.left = 0;
	bounds.bottom = bounds.right = 256;
 
	result = QTNewGWorld(&gw,k32BGRAPixelFormat,&bounds,NULL,NULL,NULL);
   
	// check for errors
	if (result != noErr)
		goto bail;
   
	// retrieve the associated HDC and HBITMAP
	hdcSrc = GetPortHDC((GrafPtr)gw);
	hbitmapSrc = GetPortHBITMAP((GrafPtr)gw);
   
	// bail if DIB allocation failed.
	if ((hdcSrc == 0) || (hbitmapSrc == 0)) {
		result = memFullErr;
		goto bail;
	}
	
	// save current port and GDevice, set current port to new GWorld
	GetGWorld(&savedPort,&savedGD);
	SetGWorld(gw,NULL);
// Render graphics into GWorld
{
	Rect macRect;
	RGBColor color;
	RECT winRect;
	HBRUSH hBrush, hBrushOld;
 
	// red
	color.red = 0xffff; color.green = 0; color.blue = 0;
	RGBForeColor(&color);
	macRect = bounds;
	PaintRect(&macRect);
 
	// green
	color.red = 0; color.green = 0xffff; color.blue = 0;
	RGBForeColor(&color);
	MacInsetRect(&macRect, 20, 20);
	PaintRect(&macRect);
 
	// blue. just for kicks lets use GDI to render graphics into the same GWorld
	MacInsetRect(&macRect, 20, 20);
	winRect.top = macRect.top;
	winRect.left = macRect.left;
	winRect.bottom = macRect.bottom;
	winRect.right = macRect.right;
	hBrush = CreateSolidBrush(RGB(0,0,0xff));
	hBrushOld = SelectObject(hdcSrc, hBrush);
	FillRect(hdcSrc, &winRect, hBrush);
	GdiFlush();
	DeleteObject(SelectObject(hdcSrc, hBrushOld));
}
    
	// copy contents of GWorld to dstHDC.
	StretchBlt(hdcDest,rectDest->left,rectDest->top,rectDest->right-rectDest->left,
		rectDest->bottom-rectDest->top,hdcSrc,bounds.left,bounds.top,
		bounds.right-bounds.left,bounds.bottom-bounds.top,SRCCOPY);
	
	// reset port
	SetGWorld(savedPort,savedGD);
   
bail:
	// dispose of gworld
	if( gw ) DisposeGWorld(gw);
	
	return result;
}

In some cases one might want to use QuickTime or Quickdraw to render into an existing DIB section. The NewGWorldFromHBITMAP call allows you to wrap an existing DIB section with a GWorld. After creating the DIBSection, call NewGWorldFromHBITMAP, passing in the HDC and HBITMAP, to create a GWorld that shares the pixels of the DIB section. As in the QTNewGWorld case the HBITMAP is selected into the HDC for the life of the GWorld. Note that in this case, Quickdraw does not dispose of the HDC or HBITMAP in DisposeGWorld, as it did not allocate them. The creator of the HDC and HBITMAP is responsible for disposing of them. Also note that the HBITMAP passed to NewGWorldFromHBITMAP must be created using the CreateDIBSection API, old style DDBs are not allowed.

OSErr RenderIntoExistingDIBExample(HDC hdcDest, RECT *rectDest)
{	
	GWorldPtr gw = nil;
	CGrafPtr savedPort;
	GDHandle savedGD;
	HDC hdcSrc,hdcTemp;
	HBITMAP hbitmapSrc;
	BITMAPINFO *bitmapInfo = nil;
	OSErr result = noErr;
	void *baseaddr;
   
	// create an HDC
	hdcTemp = GetDC(NULL);
	hdcSrc = CreateCompatibleDC(hdcTemp);
	ReleaseDC(NULL,hdcTemp);
   
	// create a DIB section
	bitmapInfo = (BITMAPINFO *)NewPtrClear(sizeof (BITMAPINFOHEADER) + (3 * sizeof (DWORD)));
   
	bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bitmapInfo->bmiHeader.biWidth = 256;
	bitmapInfo->bmiHeader.biHeight =  -1 * 256;	// top down dib
	bitmapInfo->bmiHeader.biPlanes = 1;
	bitmapInfo->bmiHeader.biBitCount = 32;
	bitmapInfo->bmiHeader.biCompression = BI_RGB;
	bitmapInfo->bmiHeader.biSizeImage = 0;
	bitmapInfo->bmiHeader.biXPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biYPelsPerMeter = 0;
	bitmapInfo->bmiHeader.biClrUsed = 0;
	bitmapInfo->bmiHeader.biClrImportant = 0;
	bitmapInfo->bmiColors[0].rgbBlue = 0;
	bitmapInfo->bmiColors[0].rgbGreen = 0;
	bitmapInfo->bmiColors[0].rgbRed = 0;
	bitmapInfo->bmiColors[0].rgbReserved = 0;
   
	hbitmapSrc = CreateDIBSection(hdcSrc, bitmapInfo, DIB_RGB_COLORS, &baseaddr, NULL, NULL);
	
	// bail if DIB creation failed
	if (hbitmapSrc == 0) {
		result = memFullErr;
		goto bail;
	}
   
	DisposePtr((Ptr)bitmapInfo);
   
	// wrap the DIB with a GWorld
	result = NewGWorldFromHBITMAP(&gw,NULL,NULL,NULL,hbitmapSrc,hdcSrc);
   
	// check for errors
	if (result != noErr)
		goto bail;
	
	// save current port and GDevice, set current port to new GWorld
	GetGWorld(&savedPort,&savedGD);
	SetGWorld(gw,NULL);
	
	// Render graphics into GWorld
	{
		Rect rect;
		RGBColor color;
   
		color.red = 0xffff; color.green = 0; color.blue = 0;
   
		RGBForeColor(&color);
   
		rect.top = rect.left = 0;
		rect.bottom = rect.right = 256;
   
		PaintRect(&rect);
   
		color.red = 0; color.green = 0xffff; color.blue = 0;
   
		RGBForeColor(&color);
   
		rect.top = rect.left = 20;
		rect.bottom = rect.right = 236;
   
		PaintRect(&rect);
	}
   
	// copy contents of GWorld to dstHDC.
	StretchBlt(hdcDest,rectDest->left,rectDest->top,
		rectDest->right-rectDest->left,rectDest->bottom-rectDest->top,
		hdcSrc,0,0,256,256,SRCCOPY);
	
	// reset port
	SetGWorld(savedPort,savedGD);
   
bail:
	// dispose of gworld	
	if( gw ) DisposeGWorld(gw);
   
	DeleteObject(hbitmapSrc);
   
	DeleteDC(hdcSrc);
	
	return result;
}

See Also

QuickTime 3 for Windows Programmers

Inside Macintosh - Imaging With Quickdraw

Change History

5/21/98 - Giovanni Agnoli - First published

6/3/98  - Giovanni Agnoli - clarify GWorld/HBITMAP relationship

Topics
Previous | Next