Clipper Fundamentals
DirectDraw can help with the bitmaps as long as the bitmaps are in the form of DirectDraw surfaces, or IDirectDrawSurface(s) to be exact.
Clipping Bitmaps the Hard Way
1、Clip each pixel of the bitmap on a independent basis as it's generated. Simple, but slow.
2、Clip the bounding rectangle of the bitmap to the viewport, and then only draw the portion of the bitmap that's within the viewport. More complex, but very fast, with almost no performance loss and no hit at all in the inner loop
Making a DirectDraw Clip with IDirectDrawClipper
- Create a DirectDraw clipper object.
- Create a clipping list.
- Send the clipping list data to the clipper with IDIRECTDRAWCLIPPER::SetClipList().
- Attach the clipper to a window and/or surface with IDIRECTDRAWSURFACE7::SetClipper().
HRESULT IDIRECTDRAW7::CreateClipper(DWORD dwFlags, // control flags
LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, // address of interface pointer
IUnknown FAR *pUnkOuter); // COM stuff
LPDIRECTDRAWCLIPPER lpddclipper = NULL; // hold the clipper
if (FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))
return(0);
typedef struct _RGNDATA
{ /* rgnd */
RGNDATAHEADER rdh; // header info
char Buffer[1]; // the actual RECT list
} RGNDATA;
typedef struct _RGNDATAHEADER
{ // rgndh
DWORD dwSize; // size of this header in bytes
DWORD iType; // type of region data
DWORD nCount; // number of RECT'S in Buffer[]
DWORD nRgnSize; // size of Buffer[]
RECT rcBound; // a bounding box around all RECTS
} RGNDATAHEADER;
HRESULT SetClipList(LPRGNDATA lpClipList, // ptr to RGNDATA
DWORD dwFlags); // flags, always 0
HRESULT SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper);
In most cases, lpddsurface would be your offscreen rendering surface, such as the back buffer surface. Usually, you don't attach a clipper to the primary surface.
LPDIRECTDRAWCLIPPER DDraw_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds,
int num_rects,
LPRECT clip_list)
{
// this function creates a clipper from the sent clip list and attaches
// it to the sent surface
int index; // looping var
LPDIRECTDRAWCLIPPER lpddclipper; // pointer to the newly
// created dd clipper
LPRGNDATA region_data; // pointer to the region
// data that contains
// the header and clip list
// first create the direct draw clipper
if (FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)))
return(NULL);
// now create the clip list from the sent data
// first allocate memory for region data
region_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)+
num_rects*sizeof(RECT));
// now copy the rects into region data
memcpy(region_data->Buffer, clip_list, sizeof(RECT)*num_rects);
// set up fields of header
region_data->rdh.dwSize = sizeof(RGNDATAHEADER);
region_data->rdh.iType = RDH_RECTANGLES;
region_data->rdh.nCount = num_rects;
region_data->rdh.nRgnSize = num_rects*sizeof(RECT);
region_data->rdh.rcBound.left = 64000;
region_data->rdh.rcBound.top = 64000;
region_data->rdh.rcBound.right = -64000;
region_data->rdh.rcBound.bottom = -64000;
// find bounds of all clipping regions
for (index=0; index<num_rects; index++)
{
// test if the next rectangle unioned with
// the current bound is larger
if (clip_list[index].left < region_data->rdh.rcBound.left)
region_data->rdh.rcBound.left = clip_list[index].left;
if (clip_list[index].right > region_data->rdh.rcBound.right)
region_data->rdh.rcBound.right = clip_list[index].right;
if (clip_list[index].top < region_data->rdh.rcBound.top)
region_data->rdh.rcBound.top = clip_list[index].top;
if (clip_list[index].bottom > region_data->rdh.rcBound.bottom)
region_data->rdh.rcBound.bottom = clip_list[index].bottom;
} // end for index
// now we have computed the bounding rectangle region and set up the data
// now let's set the clipping list
if (FAILED(lpddclipper->SetClipList(region_data, 0)))
{
// release memory and return error
free(region_data);
return(NULL);
} // end if
// now attach the clipper to the surface
if (FAILED(lpdds->SetClipper(lpddclipper)))
{
// release memory and return error
free(region_data);
return(NULL);
} // end if
// all is well, so release memory and
// send back the pointer to the new clipper
free(region_data);
return(lpddclipper);
} // end DDraw_Attach_Clipper
The function is almost trivial to use. Let's say you have an animation system with a primary surface called lpddsprimary and a secondary back buffer called lpddsback, to which you want to attach a clipper with the following RECT list:
RECT rect_list[3] = {{10,10,50,50},
{100,100,200,200},
{300,300, 500, 450}};
Here's the call to do it:
LPDIRECTDRAWCLIPPER lpddclipper =
DDraw_Attach_Clipper(lpddsback,3,rect_list);