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
|