基本图形的扫描转换(绘图函数的自定义实现)和反采样。
基本圖形的掃描轉換這個名詞不太容易理解,通俗地講就是通過SetPixel或者SetPixelV函數來實現繪圖,比如畫一條直線會使用LineTo,同樣可以用SetPixel函數實現。
其中SetPixel與SetPixelV的區別,SetPixelV不放回實際像素點的RGB值,執行速度比SetPixel快得多!!!
?
? ? ? ?圓的掃描轉換
既如何自定義繪制圓,而不是通過Ellipse直接繪制,而是通過SetPixelV去繪制。
1、根據對稱性,一個在中心在原點的圓,有4條對稱軸x=0,y=0,x=y,x=-y,分成8等分。所以只要繪制出第一象限內的1/8圓弧,通過對稱性,可以繪制出整個圓。比如第一象限內圓上某點為(x,y),則另外7個點為(x,-y),(-x,y),(-x,-y),(y,x),(-y,x),(y,-x),(-y,-x);
2、通過Bresenhma算法可以推得公式 誤差di = (xi+1)*(xi+1) + (yi-0.5)*(yi-0.5) - R*R;該公式推理過程可以看孔玲德著作計算機圖形學基礎教程(第二版96頁)。通過現有坐標(xi,yi)推斷(xi+1,yi+1)的值。如果di>>=0 yi+1 = yi-1,否則yi+1 = yi。
通過這兩個特點我們可以編寫程序,由于第一個特性需要中心在原點,所以我們要自己設置畫布的原點正好在圓心。代碼如下:
RECT rtClient;GetClientRect(hwnd,&rtClient);SIZE ptOldViewExt,ptOldWindowExt;POINT ptOldOrg;int OldMapMode = SetMapMode(hdc,MM_ANISOTROPIC);int iWidth = abs(rtClient.right - rtClient.left);int iHeight = abs(rtClient.bottom - rtClient.top);SetViewportExtEx(hdc,iWidth,iHeight,&ptOldViewExt);SetWindowExtEx(hdc,iWidth,-iHeight,&ptOldWindowExt);POINT ptOrg = {pt.x,pt.y};DPtoLP(hdc,&ptOrg,1);SetWindowOrgEx(hdc,-ptOrg.x,-ptOrg.y,&ptOldOrg);
繪制結束需要復原坐標,這是一個好習慣!
SetViewportExtEx(hdc,ptOldViewExt.cx,ptOldViewExt.cy,NULL);SetWindowExtEx(hdc,ptOldWindowExt.cx,ptOldWindowExt.cy,NULL);SetWindowOrgEx(hdc,ptOldOrg.x,ptOldOrg.y,NULL);并且根據對稱性,我們可以寫一下函數
VOID DrawAxial(HDC hdc,int x,int y,COLORREF color) {SetPixelV(hdc,x,y,color);SetPixelV(hdc,x,-y,color);SetPixelV(hdc,-x,y,color);SetPixelV(hdc,-x,-y,color);SetPixelV(hdc,y,x,color);SetPixelV(hdc,y,-x,color);SetPixelV(hdc,-y,x,color);SetPixelV(hdc,-y,-x,color); }
?
特性2代碼如下:
COLORREF color = RGB(0,0,0);double di = 0;int xi = 0,yi = R;DrawAxial(hdc,xi,yi,color);for(int i=1;i<=(int)(1.0*R/sqrt(2.0));++i){di = (xi+1)*(xi+1) + (yi-0.5)*(yi-0.5) - R*R;++xi;if(di >= 0){--yi;}DrawAxial(hdc,xi,yi,color);}
R為半徑,起點為(0,R)。通過公式可以輕而易舉地寫出代碼。
完整win32代碼如下:
#include "Main.h" #include<tchar.h> #include<stdio.h> #include<windows.h> #include<math.h>LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) {WNDCLASS wndcls;wndcls.cbClsExtra = 0;wndcls.cbWndExtra = 0;wndcls.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);wndcls.hCursor = LoadCursor(NULL,IDC_ARROW);wndcls.hIcon = LoadIcon(NULL,IDI_APPLICATION);wndcls.hInstance = hInstance;wndcls.lpfnWndProc = WinSunProc;wndcls.lpszClassName = _T("sunxin2006");wndcls.lpszMenuName = NULL;wndcls.style = CS_HREDRAW | CS_VREDRAW;RegisterClass(&wndcls);HWND hwnd = CreateWindow(_T("sunxin2006"),_T("helloworld"),WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);MSG msg;while(GetMessage(&msg,NULL,0,0)>0){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam; }VOID OnCreate(HWND hwnd,WPARAM wParam,LPARAM lParam) {int scrWidth,scrHeight;RECT rect;//獲得屏幕尺寸scrWidth = GetSystemMetrics(SM_CXSCREEN);scrHeight = GetSystemMetrics(SM_CYSCREEN);//取得窗口尺寸GetWindowRect(hwnd,&rect);//重新設置rect里的值rect.left = (scrWidth-rect.right)/2;rect.top = (scrHeight-rect.bottom)/2;//移動窗口到指定的位置 SetWindowPos(hwnd,HWND_TOP,rect.left,rect.top,rect.right,rect.bottom,SWP_SHOWWINDOW); }VOID DrawAxial(HDC hdc,int x,int y,COLORREF color) {SetPixelV(hdc,x,y,color);SetPixelV(hdc,x,-y,color);SetPixelV(hdc,-x,y,color);SetPixelV(hdc,-x,-y,color);SetPixelV(hdc,y,x,color);SetPixelV(hdc,y,-x,color);SetPixelV(hdc,-y,x,color);SetPixelV(hdc,-y,-x,color); }VOID MyDrawCircle(HWND hwnd,HDC hdc,const POINT& pt,int R) {//Ellipse(hdc,pt.x-R/2,pt.y-R/2,pt.x+R/2,pt.y+R/2); RECT rtClient;GetClientRect(hwnd,&rtClient);SIZE ptOldViewExt,ptOldWindowExt;POINT ptOldOrg;int OldMapMode = SetMapMode(hdc,MM_ANISOTROPIC);int iWidth = abs(rtClient.right - rtClient.left);int iHeight = abs(rtClient.bottom - rtClient.top);SetViewportExtEx(hdc,iWidth,iHeight,&ptOldViewExt);SetWindowExtEx(hdc,iWidth,-iHeight,&ptOldWindowExt);POINT ptOrg = {pt.x,pt.y};DPtoLP(hdc,&ptOrg,1);SetWindowOrgEx(hdc,-ptOrg.x,-ptOrg.y,&ptOldOrg);COLORREF color = RGB(0,0,0);double di = 0;int xi = 0,yi = R;DrawAxial(hdc,xi,yi,color);for(int i=1;i<=(int)(1.0*R/sqrt(2.0));++i){di = (xi+1)*(xi+1) + (yi-0.5)*(yi-0.5) - R*R;++xi;if(di >= 0){--yi;}DrawAxial(hdc,xi,yi,color);}SetViewportExtEx(hdc,ptOldViewExt.cx,ptOldViewExt.cy,NULL);SetWindowExtEx(hdc,ptOldWindowExt.cx,ptOldWindowExt.cy,NULL);SetWindowOrgEx(hdc,ptOldOrg.x,ptOldOrg.y,NULL);}VOID MyDrawCircle(HWND hwnd,HDC hdc,int iLeft,int iTop,int iRight,int iBottom) {POINT pt = {(iLeft+iRight)/2,(iTop+iBottom)/2};MyDrawCircle(hwnd,hdc,pt,abs(iRight-iLeft)/2); }VOID OnPaint(HWND hwnd,WPARAM wParam,LPARAM lParam) {RECT rtClient;GetClientRect(hwnd,&rtClient);PAINTSTRUCT ps;HDC hdc = BeginPaint(hwnd,&ps);HDC hMemDC = CreateCompatibleDC(hdc);HBITMAP hMemBmp = CreateCompatibleBitmap(hdc,rtClient.right,rtClient.bottom);HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC,hMemBmp);FillRect(hMemDC,&rtClient,WHITE_BRUSH);//POINT pt = {250,250};//MyDrawCircle(hwnd,hMemDC,pt,100);MyDrawCircle(hwnd,hMemDC,100,100,300,300);BitBlt(hdc,0,0,rtClient.right,rtClient.bottom,hMemDC,0,0,SRCCOPY);SelectObject(hMemDC,hOldBmp);DeleteObject(hMemBmp);EndPaint(hwnd,&ps); }LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) {switch(uMsg){case WM_CREATE:OnCreate(hwnd,wParam,lParam);break;case WM_PAINT:OnPaint(hwnd,wParam,lParam);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,uMsg,wParam,lParam);}return 0; } View Code
?修改:全部代碼這里的OnPaint函數中hMemDC忘記delelte了。。。
?
橢圓的掃描轉換(先貼代碼)
#include "Main.h" #include<tchar.h> #include<stdio.h> #include<windows.h> #include<math.h>LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) {WNDCLASS wndcls;wndcls.cbClsExtra = 0;wndcls.cbWndExtra = 0;wndcls.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);wndcls.hCursor = LoadCursor(NULL,IDC_ARROW);wndcls.hIcon = LoadIcon(NULL,IDI_APPLICATION);wndcls.hInstance = hInstance;wndcls.lpfnWndProc = WinSunProc;wndcls.lpszClassName = _T("sunxin2006");wndcls.lpszMenuName = NULL;wndcls.style = CS_HREDRAW | CS_VREDRAW;RegisterClass(&wndcls);HWND hwnd = CreateWindow(_T("sunxin2006"),_T("helloworld"),WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);MSG msg;while(GetMessage(&msg,NULL,0,0)>0){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam; }VOID OnCreate(HWND hwnd,WPARAM wParam,LPARAM lParam) {int scrWidth,scrHeight;RECT rect;//獲得屏幕尺寸scrWidth = GetSystemMetrics(SM_CXSCREEN);scrHeight = GetSystemMetrics(SM_CYSCREEN);//取得窗口尺寸GetWindowRect(hwnd,&rect);//重新設置rect里的值rect.left = (scrWidth-rect.right)/2;rect.top = (scrHeight-rect.bottom)/2;//移動窗口到指定的位置 SetWindowPos(hwnd,HWND_TOP,rect.left,rect.top,rect.right,rect.bottom,SWP_SHOWWINDOW); }VOID DrawAxial(HDC hdc,int x,int y,COLORREF color) {SetPixelV(hdc,x,y,color);SetPixelV(hdc,-x,y,color);SetPixelV(hdc,x,-y,color);SetPixelV(hdc,-x,-y,color); }VOID OnDrawElipse(HWND hwnd,HDC hdc,const POINT& pt,double a,double b) {//Ellipse(hdc,pt.x-a,pt.y-b,pt.x+a,pt.y+b); RECT rtClient;GetClientRect(hwnd,&rtClient);int OldMapMode = SetMapMode(hdc,MM_ANISOTROPIC);SIZE OldViewExt,OldWindowExt;SetViewportExtEx(hdc,rtClient.right,rtClient.bottom,&OldViewExt);SetWindowExtEx(hdc,rtClient.right,-rtClient.bottom,&OldWindowExt);POINT OldOrg,ptOrg = {pt.x,pt.y};DPtoLP(hdc,&ptOrg,1);SetWindowOrgEx(hdc,-ptOrg.x,-ptOrg.y,&OldOrg);COLORREF color = RGB(0,0,0);int xi = 0,yi = b;int divison = a;if(a*a+b*b !=0){divison = (int)(a*a/sqrt((a*a+b*b)));}DrawAxial(hdc,xi,yi,color);for(xi=1;xi<=divison;++xi){double di = b*b*xi*xi+a*a*(yi-0.5)*(yi-0.5) - a*a*b*b;if(di >=0) --yi;DrawAxial(hdc,xi,yi,color);}--xi;for(;yi>=0;--yi){double di = b*b*(xi+0.5)*(xi+0.5)+a*a*(yi-1)*(yi-1) - a*a*b*b;if(di < 0) ++xi;DrawAxial(hdc,xi,yi,color);}SetWindowOrgEx(hdc,OldOrg.x,OldOrg.y,NULL);SetWindowExtEx(hdc,OldWindowExt.cx,OldWindowExt.cy,NULL);SetViewportExtEx(hdc,OldViewExt.cx,OldViewExt.cy,NULL);SetMapMode(hdc,OldMapMode); } VOID OnPaint(HWND hwnd,WPARAM wParam,LPARAM lParam) {RECT rtClient;GetClientRect(hwnd,&rtClient);PAINTSTRUCT ps;HDC hdc = BeginPaint(hwnd,&ps);HDC hMemDC = CreateCompatibleDC(hdc);HBITMAP hMemBmp = CreateCompatibleBitmap(hMemDC,rtClient.right,rtClient.bottom);HBITMAP hOldBmp = (HBITMAP) SelectObject(hMemDC,hMemBmp);FillRect(hMemDC,&rtClient,WHITE_BRUSH);POINT pt = {200,200};OnDrawElipse(hwnd,hMemDC,pt,150,100);BitBlt(hdc,0,0,rtClient.right,rtClient.bottom,hMemDC,0,0,SRCCOPY);SelectObject(hMemDC,hOldBmp);DeleteObject(hMemDC);DeleteObject(hMemBmp);EndPaint(hwnd,&ps); } LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) {switch(uMsg){case WM_CREATE:OnCreate(hwnd,wParam,lParam);break;case WM_PAINT:OnPaint(hwnd,wParam,lParam);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,uMsg,wParam,lParam);}return 0; } View Code?橢圓可以分為4對稱,x軸對稱,y軸對稱。在第一象限橢圓上點為(x,y),則對稱點(x,-y),(-x,y),(-x,-y)。由于第一象限中y/x的值是有abs(k)>1到abs(k)<1或者相反。所以要先求得abs(k)=1的情況,然后根據該分界線分為兩部分。
求k=-1,其中的k為斜率。
解:
b^2*x^2 + a^2*y^2-a^2*b^2 = 0;
對其求導 2*b*x + 2*a*y*y' = 0 ? ?=> ? y1 = 1 ?=> ?a^2*y = b^2*x
帶入橢圓方程求得(a^2/sqrt(a^2+b^2),b^2/sqrt(a^2+b^2))
? ? ? ?方程b^2*x^2 + a^2*y^2-a^2*b^2 = 0;
當b<a時,如果a>b時,可以考慮一下。
第一部分x在(0,a^2/sqrt(a^2+b^2))時:
d1i = b^2*(xi+1)^2 +a^(yi-0.5)^2-a^y2b4*b^2;
yi+1 = di>=0 ? yi-1 : yi;
? 第二部分y在(b^2/sqrt(a^2+b^2))時:
d2i = b^2*(xi+0.5)^2+a^2*(yi-1)^2 - a^2*b^2;
xi+1 = d2i >= 0 ? xi:xi+1;
?
? 直線掃描轉換算法在處理非水平,非垂直,非45度的直線段時會出現鋸齒,而畫曲線更是如此!!!因此出現了反走樣技術來抗鋸齒。反走樣技術主要分為兩類:一類是硬件技術,通過提高顯示器的分別率來實現,另一類是軟件技術,通過改進算法來實現。軟件反走樣技術主要是加權區域采樣。
? ?Wu反走樣算法
該算法是采取空間混色原理來對走樣進行修正。空間混色原理指出,人眼對某一區域顏色的識別是取這個區域的平均值。Wu反走樣算法原理是對于理想直線上的任意一點,同時以兩個不同亮度等級的相鄰像素來表示。
? ? ? ?推導公式:
x^2+y^2 - R^2 = 0;
y = sqrt(R^2-X^2);
y0 = int(y),y1 = ceil(y);
最后結論(x+1,y0,255*(y-y0)),(x+1,y1,255*(y-y1));第三個是顏色。
代碼:
以下代碼是畫一個黑色圓圈,請把字體設成多字節然后編譯。
#include "Main.h" #include<tchar.h> #include<stdio.h> #include<windows.h> #include<math.h>LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) {WNDCLASS wndcls;wndcls.cbClsExtra = 0;wndcls.cbWndExtra = 0;wndcls.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);wndcls.hCursor = LoadCursor(NULL,IDC_ARROW);wndcls.hIcon = LoadIcon(NULL,IDI_APPLICATION);wndcls.hInstance = hInstance;wndcls.lpfnWndProc = WinSunProc;wndcls.lpszClassName = _T("sunxin2006");wndcls.lpszMenuName = NULL;wndcls.style = CS_HREDRAW | CS_VREDRAW;RegisterClass(&wndcls);HWND hwnd = CreateWindow(_T("sunxin2006"),_T("helloworld"),WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);MSG msg;while(GetMessage(&msg,NULL,0,0)>0){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam; }VOID OnCreate(HWND hwnd,WPARAM wParam,LPARAM lParam) {int scrWidth,scrHeight;RECT rect;//獲得屏幕尺寸scrWidth = GetSystemMetrics(SM_CXSCREEN);scrHeight = GetSystemMetrics(SM_CYSCREEN);//取得窗口尺寸GetWindowRect(hwnd,&rect);//重新設置rect里的值rect.left = (scrWidth-rect.right)/2;rect.top = (scrHeight-rect.bottom)/2;//移動窗口到指定的位置 SetWindowPos(hwnd,HWND_TOP,rect.left,rect.top,rect.right,rect.bottom,SWP_SHOWWINDOW); }VOID DrawAx(HDC hdc,int x,int y,COLORREF c) {SetPixelV(hdc,x,y,c);SetPixelV(hdc,-x,y,c);SetPixelV(hdc,x,-y,c);SetPixelV(hdc,-x,-y,c);SetPixelV(hdc,y,x,c);SetPixelV(hdc,y,-x,c);SetPixelV(hdc,-y,x,c);SetPixelV(hdc,-y,-x,c); } VOID DrawMyCircle(HWND hwnd,HDC hdc,WPARAM wParam,LPARAM lParam) {int R = 50;RECT rtClient;GetClientRect(hwnd,&rtClient);POINT pt = {300,200}; //圓心Ellipse(hdc,pt.x-R,pt.y-R,pt.x+R,pt.y+R);pt.x = 100;int OldMapMode = SetMapMode(hdc,MM_ANISOTROPIC);SIZE OldViewSize,OldWindowSize;POINT ptOldOrg;SetViewportExtEx(hdc,rtClient.right,rtClient.bottom,&OldViewSize);SetWindowExtEx(hdc,rtClient.right,-rtClient.bottom,&OldWindowSize);DPtoLP(hdc,&pt,1);SetWindowOrgEx(hdc,-pt.x,-pt.y,&ptOldOrg);double x=0,y=R;DrawAx(hdc,0,R,RGB(0,0,0));for(x=1;x<=1.0*R/sqrt(2.0);++x){y = sqrt(R*R-x*x);double y0 = int(y),y1 = ceil(y);int c0 = (int)(255.0*(y-y0)),c1 = (int)(255.0*(y1-y));DrawAx(hdc,x,y0,RGB(c0,c0,c0));DrawAx(hdc,x,y1,RGB(c1,c1,c1));}SetWindowOrgEx(hdc,ptOldOrg.x,ptOldOrg.y,NULL);SetWindowExtEx(hdc,OldWindowSize.cx,OldWindowSize.cy,NULL);SetViewportExtEx(hdc,OldViewSize.cx,OldViewSize.cy,NULL);SetMapMode(hdc,OldMapMode); } VOID OnPaint(HWND hwnd,WPARAM wParam,LPARAM lParam) {RECT rtClient;GetClientRect(hwnd,&rtClient);PAINTSTRUCT ps;HDC hdc = BeginPaint(hwnd,&ps);HDC hMemDC = CreateCompatibleDC(hdc);HBITMAP hBitmap = CreateCompatibleBitmap(hdc,rtClient.right,rtClient.bottom);SelectObject(hMemDC,hBitmap);FillRect(hMemDC,&rtClient,WHITE_BRUSH);DrawMyCircle(hwnd,hMemDC,wParam,lParam);BitBlt(hdc,0,0,rtClient.right,rtClient.bottom,hMemDC,0,0,SRCCOPY);DeleteObject(hBitmap);DeleteObject(hMemDC);EndPaint(hwnd,&ps); } LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) {switch(uMsg){case WM_PAINT:OnPaint(hwnd,wParam,lParam);break;case WM_CREATE:OnCreate(hwnd,wParam,lParam);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hwnd,uMsg,wParam,lParam);}return 0; } View Code第一個圓是經過反走樣處理的,第二個圓是未處理過的。
彩色反走樣就是線條顏色到背景顏色的漸變過程,假設線段顏色為(rf,gf,bf),背景色(rb,gb,bb),則像素顏色為RGB((rb-rf)*ei,(gb-gf)*ei,(bb-bf)*ei+bi);
轉載于:https://www.cnblogs.com/jlyg/p/8446862.html
總結
以上是生活随笔為你收集整理的基本图形的扫描转换(绘图函数的自定义实现)和反采样。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java学习之Iterator(迭代器)
- 下一篇: DC学院学习笔记(十四):总体、采样及E