参考:https://www.bilibili.com/video/BV1L24y1Z7Sz/
原理
完成代码:
// 13-边界表示的种子填充算法
// 参考 https://www.bilibili.com/video/BV1L24y1Z7Sz
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#define WINDOW_TEXT L"13-边界表示的种子填充算法"
#define ROUND(d) int(d+0.5) // 四舍五入
struct Point2 // 二维点
{
Point2() :x(0), y(0) {}
Point2(int x, int y) :x(x), y(y) {}
int x;
int y;
};
struct Stack // 栈
{
Stack* pTop; // 节点指针
Stack* pNext; // 指针域
Point2 Pixel; // 数据域
Stack()
{
pTop = NULL;
pNext = NULL;
Pixel = Point2(0, 0);
};
~Stack() {};
void Push(Point2 point) // 入栈
{
pTop = new Stack;
pTop->Pixel = point;
pTop->pNext = this->pNext;
this->pNext = pTop;
}
Point2 Pop() // 出栈
{
Point2 point;
if (this->pNext!=NULL)
{
pTop = this->pNext;
this->pNext = pTop->pNext;
point = pTop->Pixel;
delete pTop;
}
return point;
}
};
class Fill
{
public:
Stack* _pHead; // 头
Stack* _pTop; // 顶
Point2 _P[7]; // 顶点表
Point2 _seed; // 种子像素
COLORREF _seedClr; // 种子像素颜色
public:
Fill()
{
_P[0] = Point2(50, 70);
_P[1] = Point2(-150, 270);
_P[2] = Point2(-250, 20);
_P[3] = Point2(-150, -280);
_P[4] = Point2(0, -80);
_P[5] = Point2(100, -280);
_P[6] = Point2(300, 120);
_seed = Point2(0, 0);
_pHead = NULL;
_pTop = NULL;
_seedClr = RGB(0,0,255);
};
~Fill() {};
// 绘制线框
void DrawObject(HDC hdc)
{
Point2 pTemp;
for (int i = 0; i < 7; i++)
{
if (0 == i)
{
MoveToEx(hdc, _P[i].x, _P[i].y, NULL);
pTemp = _P[i];
}
else
{
LineTo(hdc, _P[i].x, _P[i].y);
}
}
LineTo(hdc, pTemp.x, pTemp.y); // 闭合线框
}
// 4联通填充算法
void BoundaryFill4(HWND hwnd,HDC hdc)
{
COLORREF boundaryClr = RGB(0, 0, 0);//边界颜色
COLORREF color;// 当前像素颜色;
int x; // 种子与图形位置
int y;
RECT rect;
GetClientRect(hwnd, &rect);
int nClientLeft = -rect.right / 2;
int nClientRight = rect.right / 2;
int nClientTop = rect.bottom / 2;
int nClientbottom = -rect.bottom / 2;
x = _seed.x - 1;
while (boundaryClr != GetPixel(hdc, x, _seed.y) && _seedClr != GetPixel(hdc, x, _seed.y)) // 是否位于多边形左侧
{
x--;
if (x < nClientLeft) // 到达客户区左段
{
MessageBox(NULL, L"种子不在客户区域图形内", L"提示", MB_OK);
return;
}
}
x = _seed.x + 1;
while (boundaryClr != GetPixel(hdc, x, _seed.y) && _seedClr != GetPixel(hdc, x, _seed.y)) // 是否位于多边形左侧
{
x++;
if (x >= nClientRight) // 到达客户区右侧
{
MessageBox(NULL, L"种子不在客户区域图形内", L"提示", MB_OK);
return;
}
}
y = _seed.y + 1;
while (boundaryClr != GetPixel(hdc, _seed.x, y) && _seedClr != GetPixel(hdc, _seed.x, y))
{
y++;
if (y >= nClientTop) // 上方
{
MessageBox(NULL, L"种子不在客户区域图形内", L"提示", MB_OK);
return;
}
}
y = _seed.y - 1;
while (boundaryClr != GetPixel(hdc, _seed.x, y) && _seedClr != GetPixel(hdc, _seed.x, y))
{
y--;
if (y <= nClientbottom) // 下方
{
MessageBox(NULL, L"种子不在客户区域图形内", L"提示", MB_OK);
return;
}
}
// 位于多边形内
_pHead = new Stack; // 建立栈头节点
_pHead->pNext = NULL;
Point2 left, top, right, bottom;// 种子四个邻节点
_pHead->Push(_seed);// 种子像素入栈
while (NULL != _pHead->pNext) // 栈不为空
{
Point2 popPoint = _pHead->Pop();
if (_seedClr == GetPixel(hdc, popPoint.x, popPoint.y))
{
continue;// 加速
}
SetPixel(hdc,popPoint.x,popPoint.y,_seedClr); // 设置种子点处颜色
left.x = popPoint.x - 1, left.y = popPoint.y; // 搜索临近点左方像素点。
color = GetPixel(hdc, left.x, left.y);
if (boundaryClr!=color&&_seedClr!=color)
{
_pHead->Push(left); // 左侧像素入栈
}
right.x = popPoint.x + 1, right.y = popPoint.y; // 搜索临近点右方像素点。
color = GetPixel(hdc, right.x, right.y);
if (boundaryClr != color && _seedClr != color)
{
_pHead->Push(right); // 右侧像素入栈
}
top.x = popPoint.x, top.y = popPoint.y+1; // 搜索临近点上方像素点。
color = GetPixel(hdc, top.x, top.y);
if (boundaryClr != color && _seedClr != color)
{
_pHead->Push(top); // 上侧像素入栈
}
bottom.x = popPoint.x, bottom.y = popPoint.y - 1; // 搜索临近点下方像素点。
color = GetPixel(hdc, bottom.x, bottom.y);
if (boundaryClr != color && _seedClr != color)
{
_pHead->Push(bottom); // 下侧像素入栈
}
}
TextOut(hdc, nClientLeft+5,nClientTop-10,L"填充完成",lstrlenW(L"填充完成"));
delete _pHead;
_pHead = NULL;
}
};
Fill g_Fill; // 填充
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN: // 点击左键填充多边形
hdc = GetDC(hwnd);
GetClientRect(hwnd, &rc);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);
SetViewportExtEx(hdc, rc.right, -rc.bottom, NULL);
SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);
g_Fill._seed.x = GET_X_LPARAM(lParam)- rc.right / 2;
g_Fill._seed.y = -(GET_Y_LPARAM(lParam) - rc.bottom / 2);
g_Fill.BoundaryFill4(hwnd, hdc);
ReleaseDC(hwnd,hdc);
return 0;
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);
SetViewportExtEx(hdc, rc.right, -rc.bottom, NULL);
SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);
g_Fill.DrawObject(hdc); // 绘制多边形线框
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"CAG";
WNDCLASS wc = { };
wc.style = CS_HREDRAW | CS_VREDRAW; // 重新绘制整个工作区
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
WINDOW_TEXT, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}