贴上示例程序。注释随便写了点。Cohen-Sutherland裁剪算法的推导?这个看代码就清楚了,没什么推导。
- /* 2008/8/29 */
- #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 BITMAP_ID 0x4D42
- #define MAX_COLORS_PALETTE 256
- typedef unsigned short USHORT;
- typedef unsigned short WORD;
- typedef unsigned char UCHAR;
- typedef unsigned char BYTE;
- //填充表面
- int DDraw_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color);
- //画线
- int Draw_Line(int x0,int y0,int x1,int y1,UCHAR color,UCHAR* vb_start,int lpitch);
- //裁剪直线
- int Clip_Line(int &x1,int &y1,int &x2,int &y2);
- //画剪切区域内的线
- int Draw_Clip_Line(int x0,int y0,int x1,int y1,UCHAR color,UCHAR* dest_buffer,int lpitch);
- //键盘按键宏
- #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 = ((SCREEN_WIDTH/2)-100),
- max_clip_x = ((SCREEN_WIDTH/2)+100),
- min_clip_y = ((SCREEN_HEIGHT/2)-100),
- max_clip_y = ((SCREEN_HEIGHT/2)+100);
- char buffer[80];
- 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);
- }
- Draw_Clip_Line(int x0,int y0,int x1,int y1,UCHAR color,UCHAR* dest_buffer,int lpitch)
- {
- int cxs,cys,cxe,cye;
- cxs=x0;
- cys=y0;
- cxe=x1;
- cye=y1;
- if(Clip_Line(cxs,cys,cxe,cye))
- Draw_Line(cxs,cys,cxe,cye,color,dest_buffer,lpitch);
- return(1);
- }
- Clip_Line(int &x1,int &y1,int &x2,int &y2)
- {/*Cohen-Sutherland裁剪算法*/
- #define CLIP_CODE_C 0x0000
- #define CLIP_CODE_N 0x0008
- #define CLIP_CODE_S 0x0004
- #define CLIP_CODE_E 0x0002
- #define CLIP_CODE_W 0x0001
- #define CLIP_CODE_NE 0x000a
- #define CLIP_CODE_SE 0x0006
- #define CLIP_CODE_NW 0x0009
- #define CLIP_CODE_SW 0x0005
- int xc1=x1,
- yc1=y1,
- xc2=x2,
- yc2=y2;
- int p1_code=0,
- p2_code=0;
- if(y1<min_clip_y)
- p1_code|=CLIP_CODE_N;
- else
- if(y1>max_clip_y)
- p1_code|=CLIP_CODE_S;
- if(x1<min_clip_x)
- p1_code|=CLIP_CODE_W;
- else
- if(x1>max_clip_x)
- p1_code|=CLIP_CODE_E;
- if(y2<min_clip_y)
- p2_code|=CLIP_CODE_N;
- else
- if(y2>max_clip_y)
- p2_code|=CLIP_CODE_S;
- if(x2<min_clip_x)
- p2_code|=CLIP_CODE_W;
- else
- if(x2>max_clip_x)
- p2_code|=CLIP_CODE_E;
- //若两点在剪切区域外且在同一方向
- if((p1_code&p2_code))
- return(0);
- //若两点都在剪切区域内
- if(p1_code==0&&p2_code==0)
- return(1);
- switch(p1_code)
- {
- case CLIP_CODE_C: break;
- case CLIP_CODE_N:
- {
- yc1=min_clip_y;
- xc1=x1+(min_clip_y-y1)*(x2-x1)/(y2-y1);
- }break;
- case CLIP_CODE_S:
- {
- yc1=max_clip_y;
- xc1=x1+(max_clip_y-y1)*(x2-x1)/(y2-y1);
- }break;
- case CLIP_CODE_W:
- {
- xc1=min_clip_x;
- yc1=y1+(min_clip_x-x1)*(y2-y1)/(x2-x1);
- }break;
- case CLIP_CODE_E:
- {
- xc1=max_clip_x;
- yc1=y1+(max_clip_x-x1)*(y2-y1)/(x2-x1);
- }break;
- case CLIP_CODE_NE:
- {
- yc1=min_clip_y;
- xc1=x1+(min_clip_y-y1)*(x2-x1)/(y2-y1);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=max_clip_x;
- yc1=y1+(max_clip_x-x1)*(y2-y1)/(x2-x1);
- }
- }break;
- case CLIP_CODE_SE:
- {
- yc1=max_clip_y;
- xc1=x1+(max_clip_y-y1)*(x2-x1)/(y2-y1);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=max_clip_x;
- yc1=y1+(max_clip_x-x1)*(y2-y1)/(x2-x1);
- }
- }break;
- case CLIP_CODE_NW:
- {
- yc1=min_clip_y;
- xc1=x1+(min_clip_y-y1)*(x2-x1)/(y2-y1);
- if(xc1<min_clip_x||xc1>max_clip_x)
- {
- xc1=min_clip_x;
- yc1=y1+(min_clip_x-x1)*(y2-y1)/(x2-x1);
- }
- }break;
- case CLIP_CODE_SW:
- {
- yc1=max_clip_y;
- xc1=x1+(max_clip_y-y1)*(x2-x1)/(y2-y1);
- if(xc1<min_clip_x||xc2>max_clip_x)
- {
- xc1=min_clip_x;
- yc1=y1+(min_clip_x-x1)*(y2-y1)/(x2-x1);
- }
- }break;
- default:break;
- }
- switch(p2_code)
- {
- case CLIP_CODE_C: break;
- case CLIP_CODE_N:
- {
- yc2=min_clip_y;
- xc2=x2+(min_clip_y-y2)*(x2-x1)/(y2-y1);
- }break;
- case CLIP_CODE_S:
- {
- yc2=max_clip_y;
- xc2=x2+(max_clip_y-y2)*(x2-x1)/(y2-y1);
- }break;
- case CLIP_CODE_W:
- {
- xc2=min_clip_x;
- yc2=y2+(min_clip_x-x2)*(y2-y1)/(x2-x1);
- }break;
- case CLIP_CODE_E:
- {
- xc2=max_clip_x;
- yc2=y2+(max_clip_x-x2)*(y2-y1)/(x2-x1);
- }break;
- case CLIP_CODE_NE:
- {
- yc2=min_clip_y;
- xc2=x2+(min_clip_y-y2)*(x2-x1)/(y2-y1);
- if(xc2<min_clip_x||xc2>max_clip_x)
- {
- xc2=max_clip_x;
- yc2=y2+(max_clip_x-x2)*(y2-y1)/(x2-x1);
- }
- }break;
- case CLIP_CODE_SE:
- {
- yc2=max_clip_y;
- xc2=x2+(max_clip_y-y2)*(x2-x1)/(y2-y1);
- if(xc2<min_clip_x||xc2>max_clip_x)
- {
- xc2=max_clip_x;
- yc2=y2+(max_clip_x-x2)*(y2-y1)/(x2-x1);
- }
- }break;
- case CLIP_CODE_NW:
- {
- yc2=min_clip_y;
- xc2=x2+(min_clip_y-y2)*(x2-x1)/(y2-y1);
- if(xc2<min_clip_x||xc2>max_clip_x)
- {
- xc2=min_clip_x;
- yc2=y2+(min_clip_x-x2)*(y2-y1)/(x2-x1);
- }
- }break;
- case CLIP_CODE_SW:
- {
- yc2=max_clip_y;
- xc2=x2+(max_clip_y-y2)*(x2-x1)/(y2-y1);
- if(xc2<min_clip_x||xc2>max_clip_x)
- {
- xc2=min_clip_x;
- yc2=y2+(min_clip_x-x2)*(y2-y1)/(x2-x1);
- }
- }break;
- default:break;
- }
- if( (xc1<min_clip_x)||(xc1>max_clip_x)||
- (yc1<min_clip_y)||(yc1>max_clip_y)||
- (xc2<min_clip_x)||(xc2>max_clip_x)||
- (yc2<min_clip_y)||(yc2>max_clip_y) )
- {/*若经过裁剪仍有点在剪切区域外,
- 则剪切区域内不存在要画的直线*/
- return (0);
- }
- /*否则修改直线的两个端点坐标*/
- x1=xc1;
- y1=yc1;
- x2=xc2;
- y2=yc2;
- return (1);
- }
- int Draw_Line(int x0,int y0,int x1,int y1,UCHAR color,UCHAR* vb_start,int lpitch)
- {/*Bresenham算法画线*/
- int dx,
- dy,
- dx2,
- dy2,
- x_inc,
- y_inc,
- error,
- index;
- vb_start=vb_start+x0+y0*lpitch;
- dx=x1-x0;
- dy=y1-y0;
- if(dx>=0)
- x_inc=1;
- else
- {
- x_inc=-1;
- dx=-dx;
- }
- if(dy>=0)
- y_inc=lpitch;
- else
- {
- y_inc=-lpitch;
- dy=-dy;
- }
- dx2=dx<<1;
- dy2=dy<<1;
- if(dx>dy)
- {/*斜率<1的情况*/
- error=dy2-dx;
- for(index=0;index<=dx;index++)
- {
- *vb_start=color;
- if(error>=0)
- {
- error-=dx2;
- vb_start+=y_inc;
- }
- error+=dy2;
- vb_start+=x_inc;
- }
- }
- else
- {/*斜率>1的情况*/
- error=dx2-dy;
- for(index=0;index<=dy;index++)
- {
- *vb_start=color;
- if(error>=0)
- {
- error-=dy2;
- vb_start+=x_inc;
- }
- error+=dx2;
- vb_start+=y_inc;
- }
- }
- return(1);
- }
- 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.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE;
- if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL)))
- 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;
- 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);
- 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_INIT_STRUCT(ddsd);
- if(FAILED(lpddsprimary->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL)))
- return(0);
- for (int index=0; index < 1000; index++)
- {
- Draw_Clip_Line(rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT,
- rand()%SCREEN_WIDTH, rand()%SCREEN_HEIGHT,
- rand()%256,
- (UCHAR *)ddsd.lpSurface, ddsd.lPitch);
- }
- if (FAILED(lpddsprimary->Unlock(NULL)))
- return(0);
- Sleep(33);
- return(1);
- }
- int Game_Shutdown(void *parms = NULL, int num_parms = 0)
- {/*清除资源*/
- if (lpddpal)
- {
- lpddpal->Release();
- lpddpal = 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);
- }
必要的优化:在Cohen-Sutherland裁剪直线算法中,由一个点的X(Y)坐标推算这个点Y(X)坐标时可以把推算的坐标+0.5,以减小误差。因为坐标在由浮点型转换为整型时会造成信息丢失。比如12.6它会转化int时为12,若加上0.5再转换则为13,自然是后者误差更小。
有关Bresenham画线算法的推导,可以参见http://www.cnblogs.com/soroman/archive/2006/07/27/509602.html