欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入浅出理解计算机图形学中的图形填充技巧(深度与广度探讨)

最编程 2024-02-13 21:02:31
...

扫描线算法:没学会。。。
2.
区域填充算法:
区域指已经表示成点阵形式的填充图形,它是象素的集合。
区域填充指先将区域的一点赋予指定的颜色,然后将该颜色扩展到整个区域的过程。区域填充算法要求区域是连通的。
主要思想(类似于走迷宫):在区域内选中一个未点亮的点,点亮它;
遍历它的上下左右(四连通法)、左上左下右上右 下(八连通法),获取像素,没有点亮就放入队列;
把队列中所有点全部取出遍历后,填充完毕。
关键点:
首先要了解画线时候对点的选取;
|k|<1:x从起始点的坐标开始:x每次变化1 -> y变化0或1
也就是每个对应的x坐标上都有一个点;
|k|>1:y从起始点的坐标开始: y每次变化1 -> x变化0或1
也就是每个对应的y坐标都有一个点;
水平线和竖直线很安全,不会有 点 “跑出” 斜线是靠点亮一个个像素点画出来的 把点连接起来即类似图中的折线
(1)递归方法:(只适合填充较小图形,否则递归层数太多会抛出异常)
四连通:

void CTestView::FloodFill4(CDC* pDC,int x, int y, int oldColor, int newColor)
	{  
		//替换颜色 
	 
			if (pDC->GetPixel(x, y) == oldColor )
			{
				pDC->SetPixel(x, y, newColor);
				FloodFill4(pDC, x, y + 1, oldColor, newColor);//上
				FloodFill4(pDC, x, y - 1, oldColor, newColor);//下
				FloodFill4(pDC, x+1, y, oldColor, newColor);//右
				FloodFill4(pDC, x-1, y , oldColor, newColor);//左

			}
		
	}

oldColor是未点亮的点的颜色即屏幕的白色:RGB(255,255,255)
newColor是想要给点点亮的颜色;

八连通:(加上左上左下右上右下四个方向)

void CTestView::FloodFill8(CDC* pDC, int x, int y, int oldColor, int newColor)
	{
		//替换颜色 

		if (pDC->GetPixel(x, y) == oldColor)
		{
			pDC->SetPixel(x, y, newColor);
			FloodFill4(pDC, x, y + 1, oldColor, newColor);//上
			FloodFill4(pDC, x, y - 1, oldColor, newColor);//下
			FloodFill4(pDC, x - 1, y+1, oldColor, newColor);//左上
			FloodFill4(pDC, x - 1, y-1, oldColor, newColor);//左下
			FloodFill4(pDC, x + 1, y+1, oldColor, newColor);//右上
			FloodFill4(pDC, x + 1, y-1, oldColor, newColor);//右下
			FloodFill4(pDC, x + 1, y, oldColor, newColor);//右
			FloodFill4(pDC, x - 1, y, oldColor, newColor);//左

		}

	}

评价:该八连通只适合填充矩形,不适合含有斜线的多边形(可能会填充到外面去 因为左上左下右上右下四个方向上的点可能在图形外)

(2)广度优先(可以填充较大图形)
四连通:

void CTestView::BFill4(CDC* pDC, int x, int y, int oldColor, int newColor)
	{	 
		CPoint a[100000];
		int k = 0;//存点
		int r = 0;//取点
		a[0] = CPoint(x,y);
		 
		//替换颜色 
		while (r <= k) {
			
			int x = a[r].x;
			int y = a[r].y;
			if (pDC->GetPixel( x,y) == oldColor )
			{
				pDC->SetPixel(x,y, newColor);
				if (pDC->GetPixel(x - 1, y) == oldColor) {//左边
					a[k++] = CPoint(x - 1, y);
				}
				
				if (pDC->GetPixel(x, y + 1) == oldColor ) {//上边
					a[k++] = CPoint(x, y + 1);
				}
				if (pDC->GetPixel(x + 1, y) == oldColor ) {//右边
					a[k++] = CPoint(x + 1, y);
				}
				if (pDC->GetPixel(x , y-1) == oldColor ) {//下边
					a[k++] = CPoint(x , y-1);
				}
			}
			r++;
		}
	}

评价:若点亮的点超过a数组大小会溢出,应该对k,r加判断。

八连通:

void CTestView::BFill8(CDC* pDC, int x, int y, int oldColor, int newColor)
	{
		int dir[8][2] = { -1,0,0,1,1,0,0,-1,-1,1,1,1,1,-1,-1,-1 };
		std::queue<CPoint> Q;
		if (pDC->GetPixel(x, y) == oldColor) {
			pDC->SetPixel(x, y, newColor);//给该点设置新颜色
			Q.push(CPoint(x, y));
		}
		while (!Q.empty()) {
			CPoint p = Q.front();
			Q.pop();
			for (int i = 4; i < 8; i++) {//左上、右上、右下、左上
				int tx = p.x + dir[i][0];
				int ty = p.y + dir[i][1];
				if (pDC->GetPixel(tx, ty) != oldColor)  continue;
				if (pDC->GetPixel(tx, p.y) != oldColor && pDC->GetPixel(p.x, ty) != oldColor) continue;//该点未点亮,但是它相邻的两个点都已点亮(说明可能是边界,那么该点在边界外所以不点亮)
				pDC->SetPixel(tx, ty, newColor);
				Q.push(CPoint(tx, ty));
			}
			for (int i = 0; i < 4; i++) {
				int tx = p.x + dir[i][0];
				int ty = p.y + dir[i][1];
				if (pDC->GetPixel(tx, ty) == oldColor) {
					pDC->SetPixel(tx, ty, newColor);
					Q.push(CPoint(tx,ty));
				}
			}
			
		}
	} 

其实我不知道在图形里面的未点亮的点会不会遇到相邻(特指)两个点已经被点亮,所以注释里用的“可能”是边界外的点,不过,图形里面的点就算这次没有被点亮,也可以下次作为别的点的八个方向上的点被点亮,或者不被点亮影响也不大吧,只要整体看上去图形是填充的就行。