可以利用前一篇文章中的Cohen-Sutherland裁剪算法对缓冲区的任意图形画出经裁剪的该图形,那么接下来该如何填充这些图形(一般是多边形)。若是四边形,我们可以把它分解成2个三角形。而对于任意三角形,又可以分解成一个平底三角形和一个平顶三角形。接下来给出填充任意三角形的代码,摘自《windows游戏编程大师技巧》。
- /* 2008/9/4
- 代码功能:填充任意三角形
- 并经过矩形裁剪*/
- #define WIN32_LEAN_AND_MEAN //不使用MFC
- #define INITGUID //使用GUID
- #include <windows.h>
- #include <windowsx.h>
- #include <mmsystem.h> //多媒体API
- #include <iostream.h>
- #include <conio.h> //控制台IO支持
- #include <stdlib.h> //声明定义的一些常用标准函数库
- #include <malloc.h> //声明或定义一些内存的函数
- #include <memory.h> //提供了内存操作相关的一些函数及声明
- #include <string.h> //字符串的一些功能
- #include <stdarg.h> //defines ANSI-style macros for variable argument functions
- #include <stdio.h> //efinitions/declarations for standard I/O routines
- #include <math.h> //一些数学方法
- #include <io.h> //declarations for low-level file handling and I/O functions
- #include <fcntl.h> //file control options used by open()
- #include <ddraw.h>
- //windows类名
- #define WINDOW_CLASS_NAME "WINCLASS1"
- //宽,高,色深(像素)
- #define SCREEN_WIDTH 640
- #define SCREEN_HEIGHT 480
- #define SCREEN_BPP 8
- #define MAX_COLORS_PALETTE 256
- typedef unsigned short USHORT;
- typedef unsigned short WORD;
- typedef unsigned char UCHAR;
- typedef unsigned char BYTE;
- //GDI文字函数
- int Draw_Text_GDI(char* text,int x,int y,int color,LPDIRECTDRAWSURFACE7 lpdds);
- //填充表面
- int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color);
- //填充任意三角形
- void Draw_Triangle_2D(int x1,int y1,int x2,int y2,int x3,int y3,int color,UCHAR *dest_buffer,int mempitch);
- //填充平顶三角形
- void Draw_Top_Tri(int x1,int y1,int x2,int y2,int x3,int y3,int color,UCHAR *dest_buffer,int mempitch);
- //填充平底三角形
- void Draw_Bottom_Tri(int x1,int y1,int x2,int y2,int x3,int y3,int color,UCHAR *dest_buffer,int mempitch);
- //键盘按键宏
- #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code)&0x8000)?1:0)
- #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code)&0x8000)?0:1)
- //初始化结构体
- #define DDRAW_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct));ddstruct.dwSize=sizeof(ddstruct); }
- HWND main_window_handle =NULL;
- int window_closed =0;
- HINSTANCE hinstance_app =NULL;
- LPDIRECTDRAW7 lpdd =NULL;
- LPDIRECTDRAWSURFACE7 lpddsprimary =NULL; //主缓存
- LPDIRECTDRAWSURFACE7 lpddsback =NULL; //后备缓冲
- LPDIRECTDRAWPALETTE lpddpal =NULL; //调色板接口
- LPDIRECTDRAWCLIPPER lpddclipper =NULL; //裁剪器
- PALETTEENTRY palette[256]; //调色板
- DDSURFACEDESC2 ddsd;
- DDBLTFX ddbltfx;
- DDSCAPS2 ddscaps;
- /*裁剪区域*/
- int min_clip_x = 0,
- max_clip_x =SCREEN_WIDTH-1,
- min_clip_y = 0,
- max_clip_y = SCREEN_HEIGHT-1;
- char buffer[80];
- int Draw_Text_GDI(char* text,int x,int y,int color,LPDIRECTDRAWSURFACE7 lpdds)
- {
- HDC xdc;
- if(FAILED(lpdds->GetDC(&xdc)))
- return(0);
- SetTextColor(xdc,RGB(palette[color].peRed,palette[color].peGreen,palette[color].peBlue));
- SetBkMode(xdc,TRANSPARENT);
- TextOut(xdc,x,y,text,strlen(text));
- lpdds->ReleaseDC(xdc);
- return(1);
- }
- int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color)
- {
- DDRAW_INIT_STRUCT(ddbltfx);
- ddbltfx.dwFillColor=color;
- lpdds->Blt(NULL,
- NULL,
- NULL,
- DDBLT_COLORFILL|DDBLT_WAIT,
- &ddbltfx);
- return(1);
- }
- //填充平顶三角形
- void Draw_Top_Tri(int x1,int y1,
- int x2,int y2,
- int x3,int y3,
- int color,
- UCHAR *dest_buffer,int mempitch)
- {
- float dx_right,
- dx_left,
- xs,xe,
- height;
- int temp_x,
- temp_y,
- right,
- left;
- UCHAR *dest_addr=NULL;
- if (x2<x1)
- {
- temp_x=x2;
- x2=x1;
- x1=temp_x;
- }
- height=y3-y1;
- dx_left=(x3-x1)/height;
- dx_right=(x3-x2)/height;
- xs=(float)x1;
- xe=(float)x2;
- if(y1<min_clip_y)
- {
- xs=xs+dx_left*(float)(-y1+min_clip_y);
- xe=xe+dx_right*(false)(-y1+min_clip_y);
- y1=min_clip_y;
- }
- if(y3>max_clip_y)
- y3=max_clip_y;
- dest_addr=dest_buffer+y1*mempitch;
- if(x1>min_clip_x&&x1<max_clip_x&
- x2>min_clip_x&&x2<max_clip_x&
- x3>min_clip_x&&x3<max_clip_x)
- {/*若三角形完全在裁剪区域内*/
- for (temp_y=y1;temp_y<=y3;temp_y++,dest_addr+=mempitch)
- {
- memset((UCHAR*)dest_addr+(size_t)xs,color,(size_t)(xe-xs+1));
- xs+=dx_left;
- xe+=dx_right;
- }
- }
- else
- {/*三角形有落在裁剪区域外的部分*/
- for (temp_y=y1;temp_y<=y3;temp_y++,dest_addr+=mempitch)
- {
- left=(int)xs;
- right=(int)xe;
- xs+=dx_left;
- xe+=dx_right;
- if(left<min_clip_x)
- {
- left=min_clip_x;
- if(right<min_clip_x)
- continue;
- }
- if(right>max_clip_x)
- {
- right=max_clip_x;
- if(left>max_clip_x)
- continue;
- }
- memset((UCHAR*)dest_addr+(size_t)left,color,(size_t)(right-left+1));
- }
- }
- }
- //填充平底三角形
- void Draw_Bottom_Tri(int x1,int y1,
- int x2,int y2,
- int x3,int y3,
- int color,
- UCHAR *dest_buffer, int mempitch)
- {
- // this function draws a triangle that has a flat bottom
- float dx_right, // the dx/dy ratio of the right edge of line
- dx_left, // the dx/dy ratio of the left edge of line
- xs,xe, // the starting and ending points of the edges
- height; // the height of the triangle
- int temp_x, // used during sorting as temps
- temp_y,
- right, // used by clipping
- left;
- // destination address of next scanline
- UCHAR *dest_addr;
- // test order of x1 and x2
- if (x3 < x2)
- {
- temp_x = x2;
- x2 = x3;
- x3 = temp_x;
- } // end if swap
- // compute delta's
- height = y3-y1;
- dx_left = (x2-x1)/height;
- dx_right = (x3-x1)/height;
- // set starting points
- xs = (float)x1;
- xe = (float)x1; // +(float)0.5;
- // perform y clipping
- if (y1<min_clip_y)
- {
- // compute new xs and ys
- xs = xs+dx_left*(float)(-y1+min_clip_y);
- xe = xe+dx_right*(float)(-y1+min_clip_y);
- // reset y1
- y1=min_clip_y;
- } // end if top is off screen
- if (y3>max_clip_y)
- y3=max_clip_y;
- // compute starting address in video memory
- dest_addr = dest_buffer+y1*mempitch;
- // test if x clipping is needed
- if (x1>=min_clip_x && x1<=max_clip_x &
- x2>=min_clip_x && x2<=max_clip_x &
- x3>=min_clip_x && x3<=max_clip_x)
- {
- // draw the triangle
- for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=mempitch)
- {
- memset((UCHAR *)dest_addr+(unsigned int)xs,
- color,(unsigned int)(xe-xs+1));
- // adjust starting point and ending point
- xs+=dx_left;
- xe+=dx_right;
- } // end for
- } // end if no x clipping needed
- else
- {
- // clip x axis with slower version
- // draw the triangle
- for (temp_y=y1; temp_y<=y3; temp_y++,dest_addr+=mempitch)
- {
- // do x clip
- left = (int)xs;
- right = (int)xe;
- // adjust starting point and ending point
- xs+=dx_left;
- xe+=dx_right;
- // clip line
- if (left < min_clip_x)
- {
- left = min_clip_x;
- if (right < min_clip_x)
- continue;
- }
- if (right > max_clip_x)
- {
- right = max_clip_x;
- if (left > max_clip_x)
- continue;
- }
- memset((UCHAR *)dest_addr+(unsigned int)left,
- color,(unsigned int)(right-left+1));
- } // end for
- } // end else x clipping needed
- } // end Draw_Bottom_Tri
- //填充三角形
- void Draw_Triangle_2D(int x1,int y1,
- int x2,int y2,
- int x3,int y3,
- int color,
- UCHAR *dest_buffer,int mempitch)
- {
- int temp_x,
- temp_y,
- new_x;
- //三角形x||y坐标在同一直线
- if ((x1==x2&&x2==x3)||(y1==y2&&y2==y3))
- return;
- //确定三角形三点的位置
- if (y2<y1)
- {
- temp_x=x2;
- temp_y=y2;
- x2=x1;
- y2=y1;
- x1=temp_x;
- y1=temp_y;
- }
- if (y3<y1)
- {
- temp_x=x3;
- temp_y=y3;
- x3=x1;
- y3=y1;
- x1=temp_x;
- y1=temp_y;
- }
- if (y3<y2)
- {
- temp_x=x3;
- temp_y=y3;
- x3=x2;
- y3=y2;
- x2=temp_x;
- y2=temp_y;
- }
- if (y3<min_clip_y||y1>max_clip_y||
- (x1<min_clip_x&&x2<min_clip_x&&x3<min_clip_x)||
- (x1>max_clip_x&&x2>max_clip_x&&x3>max_clip_x))
- return;
- if(y1==y2)
- Draw_Top_Tri(x1,y1,x2,y2,x3,y3,color,dest_buffer,mempitch);
- else
- if(y2==y3)
- Draw_Bottom_Tri(x1,y1,x2,y2,x3,y3,color,dest_buffer,mempitch);
- else
- {/*三角形是任意的*/
- new_x=x1+(int)(0.5+(float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1));
- Draw_Bottom_Tri(x1,y1,new_x,y2,x2,y2,color,dest_buffer,mempitch);
- Draw_Top_Tri(x2,y2,new_x,y2,x3,y3,color,dest_buffer,mempitch);
- }
- }
- LRESULT CALLBACK WindowProc(HWND hwnd,
- UINT msg,
- WPARAM wparam,
- LPARAM lparam)
- {
- PAINTSTRUCT ps;
- HDC hdc;
- switch(msg)
- {
- case WM_CREATE:
- {
- return(0);
- } break;
- case WM_PAINT:
- {
- hdc = BeginPaint(hwnd,&ps);
- EndPaint(hwnd,&ps);
- return(0);
- } break;
- case WM_DESTROY:
- {
- PostQuitMessage(0);
- return(0);
- } break;
- default:break;
- }
- return (DefWindowProc(hwnd, msg, wparam, lparam));
- }
- int Game_Init(void *parms=NULL,int num_parms=0)
- {
- if(FAILED(DirectDrawCreateEx(NULL,(void **)&lpdd,IID_IDirectDraw7,NULL)))
- return(0);
- if(FAILED(lpdd->SetCooperativeLevel(main_window_handle,
- DDSCL_FULLSCREEN|DDSCL_ALLOWMODEX|
- DDSCL_EXCLUSIVE|DDSCL_ALLOWREBOOT)))
- return(0);
- if(FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,0,0)))
- return(0);
- DDRAW_INIT_STRUCT(ddsd);
- ddsd.dwFlags=DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
- ddsd.dwBackBufferCount=1;
- ddsd.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE|DDSCAPS_COMPLEX|
- DDSCAPS_FLIP;
- if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)))
- return(0);
- ddsd.ddsCaps.dwCaps=DDSCAPS_BACKBUFFER;
- if(FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps,&lpddsback)))
- return(0);
- for (int color=1; color < 255; color++)
- {
- palette[color].peRed = rand()%256;
- palette[color].peGreen = rand()%256;
- palette[color].peBlue = rand()%256;
- palette[color].peFlags = PC_NOCOLLAPSE;
- }
- palette[0].peRed = 0;
- palette[0].peGreen = 0;
- palette[0].peBlue = 0;
- palette[0].peFlags = PC_NOCOLLAPSE;
- palette[255].peRed = 255;
- palette[255].peGreen = 255;
- palette[255].peBlue = 255;
- palette[255].peFlags = PC_NOCOLLAPSE;
- palette[1].peRed = 255;
- palette[1].peGreen = 0;
- palette[1].peBlue = 0;
- palette[1].peFlags = PC_NOCOLLAPSE;
- if(FAILED(lpdd->CreatePalette(DDPCAPS_8BIT|DDPCAPS_ALLOW256|
- DDPCAPS_INITIALIZE,palette,&lpddpal,NULL)))
- return(0);
- if(FAILED(lpddsprimary->SetPalette(lpddpal)))
- return(0);
- DDraw_Fill_Surface(lpddsprimary,0);
- DDraw_Fill_Surface(lpddsback,0);
- return(1);
- }
- int Game_Main(void *parms = NULL, int num_parms = 0)
- {
- if(window_closed)
- return(0);
- if(KEYDOWN(VK_ESCAPE))
- {
- PostMessage(main_window_handle,WM_CLOSE,0,0);
- window_closed=1;
- }
- DDraw_Fill_Surface(lpddsback,0);
- DDRAW_INIT_STRUCT(ddsd);
- if(FAILED(lpddsback->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)))
- return(0);
- Draw_Triangle_2D(rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT,
- rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT,
- rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT,
- rand()%256,(UCHAR *)ddsd.lpSurface, ddsd.lPitch);
- if (FAILED(lpddsback->Unlock(NULL)))
- return(0);
- while(FAILED(lpddsprimary->Flip(NULL,DDFLIP_WAIT)));
- Sleep(33);
- return(1);
- }
- int Game_Shutdown(void *parms = NULL, int num_parms = 0)
- {/*清除资源*/
- if (lpddpal)
- {
- lpddpal->Release();
- lpddpal = NULL;
- }
- if(lpddsback)
- {
- lpddsback->Release();
- lpddsback=NULL;
- }
- if (lpddsprimary)
- {
- lpddsprimary->Release();
- lpddsprimary = NULL;
- }
- if (lpdd)
- {
- lpdd->Release();
- lpdd = NULL;
- }
- return(1);
- }
- int WINAPI WinMain( HINSTANCE hinstance,
- HINSTANCE hprevinstance,
- LPSTR lpcmdline,
- int ncmdshow)
- {
- WNDCLASSEX winclass;
- HWND hwnd;
- MSG msg;
- //HDC hdc;
- winclass.cbSize = sizeof(WNDCLASSEX);
- winclass.style = CS_DBLCLKS | CS_OWNDC |
- CS_HREDRAW | CS_VREDRAW;
- winclass.lpfnWndProc = WindowProc;
- winclass.cbClsExtra = 0;
- winclass.cbWndExtra = 0;
- winclass.hInstance = hinstance;
- winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
- winclass.lpszMenuName = NULL;
- winclass.lpszClassName = WINDOW_CLASS_NAME;
- winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
- hinstance_app = hinstance;
- if (!RegisterClassEx(&winclass))
- return(0);
- if (!(hwnd = CreateWindowEx(NULL,
- WINDOW_CLASS_NAME,
- "DirectDraw 8-Bit Line Drawing Demo",
- WS_POPUP | WS_VISIBLE,
- 0,0,
- SCREEN_WIDTH,SCREEN_HEIGHT,
- NULL,
- NULL,
- hinstance,
- NULL)))
- return(0);
- main_window_handle = hwnd;
- Game_Init();
- while(TRUE)
- {
- if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
- {
- if (msg.message == WM_QUIT)
- break;
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- Game_Main();
- }
- Game_Shutdown();
- return(msg.wParam);
- }
若是四边形则可以分解成2个三角形来进行填充。
- inline void Draw_Quad_2D(int x0,int y0,
- int x1,int y1,
- int x2,int y2,
- int x3, int y3,
- int color,
- UCHAR *dest_buffer, int mempitch)
- {//填充任意四边形
- Draw_Triangle_2D(x0,y0,x1,y1,x3,y3,color,dest_buffer,mempitch);
- Draw_Triangle_2D(x1,y1,x2,y2,x3,y3,color,dest_buffer,mempitch);
- }
这里把填充四边形的函数设为inline类型,因为该函数够简短,避免了无谓的压栈出栈操作。