达到边界颜色时,填充无法填充整个形状?
Posted
技术标签:
【中文标题】达到边界颜色时,填充无法填充整个形状?【英文标题】:Flood-fill fails to fill whole shape when reaching boundary color? 【发布时间】:2019-04-04 11:12:59 【问题描述】:我想画一些形状,然后用选定的颜色填充这些形状。此时,我刚刚完成了一些基本形状的绘制,但我仍然停留在使用 Flood-fill 算法为它们着色。
具体来说,点击后我无法为整个形状着色。它所能做的只是在屏幕边缘画一条线,将所选颜色泄漏到形状边缘之外(忽略形状的边界颜色)。
这是我所有的工作,谁能帮帮我???
#include <GL/glut.h>
#include <math.h>
#include <iostream>
using namespace std;
static int window;
static int value = 0;
GLsizei width, height;
int flag = 0;
bool up = false, down = false;
struct Point
GLint x;
GLint y;
;
struct RGBColor
GLfloat r;
GLfloat g;
GLfloat b;
;
struct Position
Position() : x(0), y(0)
float x;
float y;
;
Position start;
Position finish;
void init()
glClearColor(0, 0, 0, 0);
glPointSize(2.0);
glLineWidth(2.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 500.0, 500.0, 0.0);
void menu(int num)
if (num == 0)
glutDestroyWindow(window);
exit(0);
else
value = num;
glutPostRedisplay();
void createMenu(void)
int sub_Triangle = glutCreateMenu(menu);
glutAddMenuEntry("Isoceles right", 2);
glutAddMenuEntry("Equilateral", 3);
int menu_Triangle = glutCreateMenu(menu);
int sub_Quadrilateral = glutCreateMenu(menu);
glutAddMenuEntry("Rectangle", 4);
glutAddMenuEntry("Square", 5);
int menu__Quadrilateral = glutCreateMenu(menu);
int sub_Oval = glutCreateMenu(menu);
glutAddMenuEntry("Circle", 6);
glutAddMenuEntry("Ellipse", 7);
int menu_Oval = glutCreateMenu(menu);
int sub_RegularPolygon = glutCreateMenu(menu);
glutAddMenuEntry("Pentagon", 8);
glutAddMenuEntry("Hexagon", 9);
int menu_RegularPolygon = glutCreateMenu(menu);
int sub_OtherShape = glutCreateMenu(menu);
glutAddMenuEntry("Arrow", 10);
glutAddMenuEntry("Star", 11);
int menu_OtherShape = glutCreateMenu(menu);
int sub_Operation = glutCreateMenu(menu);
glutAddMenuEntry("Add", 12);
glutAddMenuEntry("Subtract", 13);
glutAddMenuEntry("Multiply", 14);
glutAddMenuEntry("Divide", 15);
int menu_Operation = glutCreateMenu(menu);
int sub_ColorFill = glutCreateMenu(menu);
glutAddMenuEntry("Red", 16);
glutAddMenuEntry("Green", 17);
glutAddMenuEntry("Blue", 18);
int menu_ColorFill = glutCreateMenu(menu);
glutAddMenuEntry("Line", 1);
glutAddSubMenu("Triangle", sub_Triangle);
glutAddSubMenu("Quadrilateral", sub_Quadrilateral);
glutAddSubMenu("Oval", sub_Oval);
glutAddSubMenu("Regular Polygon", sub_RegularPolygon);
glutAddSubMenu("Other Shape", sub_OtherShape);
glutAddSubMenu("Operation", sub_Operation);
glutAddSubMenu("Color Fill", sub_ColorFill);
glutAddMenuEntry("Shape Choice", 19);
glutAttachMenu(GLUT_RIGHT_BUTTON);
void draw_Line()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
glVertex2f(start.x, start.y);
glVertex2f(finish.x, finish.y);
glEnd();
glutSwapBuffers();
void draw_IsocelesRight()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
int a = finish.y - start.y;
glVertex2f(start.x, start.y);
glVertex2f(start.x, start.y + a);
glVertex2f(start.x + a, start.y + a);
void draw_Equilateral()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
int midx = (finish.x + start.x) / 2;
int midbotx = (start.y - finish.y)*(1 / sqrt(3));
int midboty = finish.y;
glVertex2f(midx, start.y);
glVertex2f(midx - midbotx, finish.y);
glVertex2f(midx + midbotx, finish.y);
void draw_Rectangle()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
glVertex2f(start.x, start.y);
glVertex2f(finish.x, start.y);
glVertex2f(finish.x, finish.y);
glVertex2f(start.x, finish.y);
void draw_Square()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
int a = finish.y - start.y;
glVertex2f(start.x, start.y);
glVertex2f(start.x, start.y + a);
glVertex2f(start.x + a, start.y + a);
glVertex2f(start.x + a, start.y);
void draw_Circle()
float PI = 3.14;
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
int size = sqrt(pow(finish.x - start.x, 2) + pow(finish.y - start.y, 2));
for (int i = 0; i <= 360; i++)
float theta = (2 * 3.14 * i) / 360;
glVertex2f((size / 2) * cos(theta) + finish.x, (size / 2) * sin(theta) + finish.y);
void draw_Ellipse()
float PI = 3.14;
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
int size1 = sqrt(pow(finish.x - start.x, 2) + pow(finish.x - start.x, 2));
int size2 = sqrt(pow(finish.y - start.y, 2) + pow(finish.y - start.y, 2));
for (int i = 0; i <= 360; i++)
float theta = (2 * 3.14 * i) / 360;
glVertex2f((size1 / 2) * cos(theta) + finish.x, (size2 / 2) * sin(theta) + finish.y);
void draw_Pentagon()
const double PI = 3.14159265358979323846;
double r = sqrt(pow(finish.x - start.x, 2));
int sides = 5;
int center_x = start.x + (finish.x - start.x) / 2;
int center_y = start.y + (finish.y - start.y) / 2;
glBegin(GL_LINE_LOOP);
for (int i = 0; i < sides; i++)
double angle = i * 2 * PI / sides;
glVertex2d(center_x + r*cos(angle), center_y + r*sin(angle));
void draw_Hexagon()
const double PI = 3.14159265358979323846;
double r = sqrt(pow(finish.x - start.x, 2));
int sides = 6;
int center_x = start.x + (finish.x - start.x) / 2;
int center_y = start.y + (finish.y - start.y) / 2;
glBegin(GL_LINE_LOOP);
for (int i = 0; i < sides; i++)
double angle = i * 2 * PI / sides;
glVertex2d(center_x + r*cos(angle), center_y + r*sin(angle));
void draw_Arrow()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
const double PI = 3.14159265358979323846;
double length = finish.x - start.x;
double e = length / 3;
double x1 = finish.x - e * (1/tan(45 * PI /180));
double y1 = start.y - e * tan(45 * PI / 180);
double x2 = finish.x - e * (1 / tan(45 * PI / 180));
double y2 = start.y + e * tan(45 * PI / 180);
glVertex2f(start.x, start.y);
glVertex2f(finish.x, start.y);
glVertex2f(finish.x, start.y);
glVertex2f(x1, y1);
glVertex2f(finish.x, start.y);
glVertex2f(x2, y2);
void draw_Star()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_LOOP);
glVertex2f(100, 225);
glVertex2f(210, 225);
glVertex2f(250, 100);
glVertex2f(290, 225);
glVertex2f(400, 225);
glVertex2f(315, 290);
glVertex2f(350, 400);
glVertex2f(250, 330);
glVertex2f(150, 400);
glVertex2f(185, 290);
void draw_Add()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
double length = finish.x - start.x;
double half_length = length / 2;
glVertex2f(start.x + half_length, start.y);
glVertex2f(start.x + half_length, start.y + length);
glVertex2f(start.x, start.y + half_length);
glVertex2f(start.x + length, start.y + half_length);
void draw_Subtract()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
double length = finish.x - start.x;
glVertex2f(start.x, start.y);
glVertex2f(start.x + length, start.y);
void draw_Multiply()
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
double length = finish.x - start.x;
glVertex2f(start.x, start.y);
glVertex2f(start.x + length, start.y + length);
glVertex2f(start.x + length , start.y);
glVertex2f(start.x, start.y + length);
RGBColor getPixelColor(GLint x, GLint y)
RGBColor color;
glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, &color);
return color;
void setPixelColor(GLint x, GLint y, RGBColor color)
glColor3f(color.r, color.g, color.b);
glBegin(GL_POINTS);
glVertex2i(x, y);
glEnd();
glFlush();
void floodFill(GLint x, GLint y, RGBColor oldColor, RGBColor newColor)
RGBColor color;
color = getPixelColor(x, y);
if (color.r == oldColor.r && color.g == oldColor.g && color.b == oldColor.b)
setPixelColor(x, y, newColor);
floodFill(x + 1, y, oldColor, newColor);
floodFill(x, y + 1, oldColor, newColor);
floodFill(x - 1, y, oldColor, newColor);
floodFill(x, y - 1, oldColor, newColor);
return;
bool IsSameColor(RGBColor x, RGBColor y)
if (x.r == y.r && x.b == y.b && x.g == y.g)
cout << "is same color true" << endl;
return true;
else
cout << "is same color false" << endl;
return false;
void BoundaryFill(int x, int y, RGBColor F_Color, RGBColor B_Color)
cout << "bound fill" << endl;
RGBColor currentColor;
currentColor = getPixelColor(x, y);
if (!IsSameColor(currentColor, B_Color) && !IsSameColor(currentColor, F_Color))
cout << "bound" << endl;
setPixelColor(x, y, F_Color);
BoundaryFill(x - 1, y, F_Color, B_Color);
BoundaryFill(x, y + 1, F_Color, B_Color);
BoundaryFill(x + 1, y, F_Color, B_Color);
BoundaryFill(x, y - 1, F_Color, B_Color);
void display()
if (flag == 1)
if (value == 1)
draw_Line();
else if (value == 2)
draw_IsocelesRight();
else if (value == 3)
draw_Equilateral();
else if (value == 4)
draw_Rectangle();
else if (value == 5)
draw_Square();
else if (value == 6)
draw_Circle();
else if (value == 7)
draw_Ellipse();
else if (value == 8)
draw_Pentagon();
else if (value == 9)
draw_Hexagon();
else if (value == 10)
draw_Arrow();
else if (value == 11)
draw_Star();
else if (value == 12)
draw_Add();
else if (value == 13)
draw_Subtract();
else if (value == 14)
draw_Multiply();
else if (value == 16)
RGBColor newColor = 1.0f, 0.0f, 0.0f ; // red
RGBColor oldColor = 0.0f, 0.0f, 0.0f ; // black
floodFill(start.x, start.y, oldColor, newColor);
glEnd();
glutSwapBuffers();
glFlush();
flag = 0;
void reshape(int w, int h)
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, w, h, 0);
width = w;
height = h;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
void mouse(int button, int state, int x, int y)
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
down = true;
start.x = x; //x1
start.y = y; //y1
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
if (down == true)
up = true;
flag = 1;
finish.x = x;
finish.y = y;
down = false;
up = false;
glutPostRedisplay();
int main(int argc, char** argv)
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(450, 100);
window = glutCreateWindow("Coloring Shapes");
init();
createMenu();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
【问题讨论】:
除非您这样做是为了练习,否则我建议使用GL_TRIANGLES
或类似的东西绘制填充形状。你目前的做法总是慢。
【参考方案1】:
主要问题是,您传递给glReadPixels
的坐标是错误的。
当一个点被glVertex
绘制时,坐标会被模型视图和投影矩阵转换。
所以应用了正交投影
gluOrtho2D(0.0, 500.0, 500.0, 0.0);
但是glReadPixels
直接读取了framebuffer的fragment。坐标是帧缓冲区中片段的 x 和 y 坐标,其中左下角始终为 (0, 0),右上角为 (widht, height) .
阅读片段时必须翻转 y 坐标:
RGBColor getPixelColor(GLint x, GLint y)
RGBColor color;
glReadPixels(x, height-y, 1, 1, GL_RGB, GL_FLOAT, &color);
return color;
为了在调用glVertex2i
时不会出现舍入错误,我建议改用glRasterPos2i
和glDrawPixels
。 glRasterPos2i
的坐标由当前模型视图和投影矩阵转换:
void setPixelColor(GLint x, GLint y, RGBColor color)
glRasterPos2i( x, y );
glDrawPixels( 1, 1, GL_RGB, GL_FLOAT, &color );
glFlush();
由于颜色值是浮点值,因此在从 GPU 读取颜色时必须考虑不准确。比较颜色时请考虑这一点:
例如
color1.r == color2.r
fabs(color1.r - color2.r) < 0.001f
编写一个比较颜色的函数并在floodFill
中使用它:
bool isEqual( const RGBColor &c1, const RGBColor &c2 )
return fabs(c1.r - c2.r) < 0.001f && fabs(c1.g - c2.g) < 0.001f && fabs(c1.b - c2.b) < 0.001f;
void floodFill(GLint x, GLint y, RGBColor oldColor, RGBColor newColor)
RGBColor color;
color = getPixelColor(x, y);
if ( isEqual(color, oldColor) )
setPixelColor(x, y, newColor);
floodFill(x + 1, y, oldColor, newColor);
floodFill(x, y + 1, oldColor, newColor);
floodFill(x - 1, y, oldColor, newColor);
floodFill(x, y - 1, oldColor, newColor);
return;
无论如何,您的代码仅适用于小区域,因为该算法是递归的。堆栈使用量迅速增加,并立即导致堆栈溢出。 但它适用于尖齿区域。
【讨论】:
【参考方案2】:确保您的 glReadBuffer()
指向正确的缓冲区。对于双缓冲渲染,glDrawBuffer
和 glReadBuffer
默认指向 GL_BACK
。
您的代码中也存在一些问题。在我看来,您以不正确的方式使用双缓冲渲染。单帧渲染过程通常是这样的:
void display()
// user sees front buffer at the moment
ClearScreen(); // glClear() or something, done on back buffer
RenderAllYouHaveOnTheScene(); // rendered to back buffer
SwapBuffers(); // glutSwapBuffers() does this in glut
// Now back buffer becomes front buffer and user sees rendered scene.
// Contents of current back buffer (which was front buffer
// before glutSwapBuffers()) is undefined and you should
// clear it and redraw everything on next iteration.
而且我看不出在您的draw_Line()
过程中交换缓冲区有任何意义。
用 == 操作符做浮点比较不是一件好事。阅读here。使用unsigned char
像素格式或进行浮点比较,如链接中所述。
还要检查您的 (x, y) 坐标是否超出屏幕边界(0
编辑:另外,正如我后来注意到的,您在glutInitDisplayMode
中指定了GLUT_SINGLE
,这意味着单缓冲渲染,但使用glutSwapBuffers
,它只应在双缓冲的情况下使用渲染。阅读here 了解其中的区别。
【讨论】:
我所做的一切都参考了这个网站:***.com/questions/8000921/…,他们也对浮点数进行了比较,我也是。我尝试运行他们的代码,效果很好。我不认为浮点比较是在这里出错的原因。 @magicww 那里的代码按照我描述的方式进行双缓冲渲染,但你没有。特别注意答案中的draw_Line()
备注。它还使用整数作为像素格式而不是浮点数,并且不对此进行比较。
你也可以只使用单缓冲方法。
请注意,在泛洪之前,您必须先为泛洪填充绘制边界区域。在双缓冲渲染中,您应该在每一帧都这样做或保存所有像素并在每一帧重绘它们,因为交换缓冲区并不能保证保存缓冲区内容,而且即使内容被保留,它也已经过时了。
@magicww 如果您在谈论列表中的最后一个答案,那么我认为它也有问题。回答“他们也对浮动进行了比较,我也做了”。网站上的答案不保证正确或没有错误。以上是关于达到边界颜色时,填充无法填充整个形状?的主要内容,如果未能解决你的问题,请参考以下文章