-2

我在我的 ipad 绘画应用程序上使用递归洪水填充算法,我认为它会因堆栈溢出而崩溃。有人可以用示例代码或好的建议帮助我解决这个问题,因为我是菜鸟吗?

-(void)paintingBucket:(int)point point2:(int)point2 width:(int)width colorAtPoint:(UIColor *)color {

int offset = 0;
int x = point;
int y = point2;
offset = 4*((width*round(y))+round(x));

if (((x<1025 && y<705)||(x<1025) ||(y<705)) && (x>0 && y>0) && (offset<2887648)) {

    int alpha = data[offset];
    int red = data[offset + 1];
    int green = data[offset + 2];
    int blue = data[offset + 3];
    color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];

        if (![color1 isEqual: color] ) {
            return;
        } else {
            color3 = self.currentColor ;
            CGFloat r,g,b,a;
            [color3 getRed:&r green:&g blue: &b alpha: &a];
            int reda = (int)(255.0 * r);
            int greena = (int)(255.0 * g);
            int bluea = (int)(255.0 * b);
            int alphaa = (int)(255.0 * a);
            // NSLog(@" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);

            data[offset + 3] = alphaa;
            data[offset + 2] = reda;
            data[offset + 1] = greena;
            data[offset] = bluea;
        }
    }

    [self paintingBucket:x+1 point2:y    width:width colorAtPoint:color];
    [self paintingBucket:x-1 point2:y    width:width colorAtPoint:color];
    [self paintingBucket:x   point2:y+1  width:width colorAtPoint:color];
    [self paintingBucket:x   point2:y-1  width:width colorAtPoint:color];
}
4

4 回答 4

1

这是一个幼稚的例子,它使它成为一个动态算法,而不是递归的。

NSMutableArray *pointsToRender = [NSMutableArray new];
[pointsToRender addObject:startingPoint];

while (pointsToRender.length>0) {

    // Get a point from the array and fill it
    MyPoint *point = [pointsToRender lastObject];
    [pointsToRender removeLastObject];
    [self drawColor:color atPoint:point];

    // Are any surrounding points needing to be filled?
    if (point 1px above) needs to be filled
        [pointsToRender addObject : point1Above];
    .. repeat for the other three points

}

是的,这是半客观的 C 和半伪代码,对不起。但你明白了。在英语中是这样的:

  • 制作一个需要填充的点数组,其中包含起点。
  • 对于每个点
    • 填充
    • 从数组中删除它
    • 看看它的邻居。如果还需要填充每个邻居,则将其添加到数组中
    • 重复直到要填充的点数组为空

这将消耗堆,而不是堆栈。

于 2012-09-10T10:53:30.913 回答
0

我用这两种方法解决了我的问题。它有点慢,但我正在努力优化这个解决方案。

-(void)fillRight:(int)x point2:(int)y witdh:(int)width {
int x1 = x;
int y1 = y;

int offset = 4*((width*round(y1))+round(x1));
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
// NSLog(@"%d %d %@ %@", x,y,color,color1);

if([color1 isEqual: color])
{
    color3 = self.currentColor ;
    CGFloat r,g,b,a;
    [color3 getRed:&r green:&g blue: &b alpha: &a];
    int reda = (int)(255.0 * r);
    int greena = (int)(255.0 * g);
    int bluea = (int)(255.0 * b);
    int alphaa = (int)(255.0 * a);
    // NSLog(@" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);

    data[offset + 3] = alphaa;
    data[offset + 2] = reda;
    data[offset + 1] = greena;
    data[offset] = bluea;


    [self fillRight:++x1 point2:y1 witdh:width];
    x1 = x1 - 1 ;
    [self fillRight:x1 point2:y1-1 witdh:width];
    [self fillRight:x1 point2:y1+1 witdh:width];
}
}

-(void)fillLeft:(int)x point2:(int)y width:(int)width {
int x1 = x;
int y1 = y;

int offset = 4*((width*round(y1))+round(x1));
int alpha = data[offset];
int red = data[offset + 1];
int green = data[offset + 2];
int blue = data[offset + 3];
color1 = [UIColor colorWithRed:(green/255.0f) green:(red/255.0f) blue:(alpha/255.0f) alpha:(blue/255.0f)];
// NSLog(@"%d %d %@ %@", x,y,color,color1);

if([color1 isEqual: color])
{
    color3 = self.currentColor ;
    CGFloat r,g,b,a;
    [color3 getRed:&r green:&g blue: &b alpha: &a];
    int reda = (int)(255.0 * r);
    int greena = (int)(255.0 * g);
    int bluea = (int)(255.0 * b);
    int alphaa = (int)(255.0 * a);
    // NSLog(@" red: %u green: %u blue: %u alpha: %u", reda, greena, bluea, alphaa);

    data[offset + 3] = alphaa;
    data[offset + 2] = reda;
    data[offset + 1] = greena;
    data[offset] = bluea;

    [self fillLeft:--x1 point2:y1 width:width];
    x1 = x1 + 1 ;
    [self fillLeft:x1 point2:y1-1 width:width];
    [self fillLeft:x1 point2:y1+1 width:width];
}
}
于 2012-09-14T09:16:45.807 回答
0

我个人更喜欢使用 A* 算法进行洪水填充。基本上,您为访问的节点着色。在搜索已知位于填充区域之外的点的路径时。(-1,-1 可以解决问题)

于 2012-11-16T16:36:14.523 回答
0

将函数分解为 fillRight 和 fillLeft 方法并不是长久之计,因为如果图像变大,可能会再次发生溢出。

我建议使用更快的洪水填充算法。Wikipedia 上描述的 4-Way 洪水填充很容易实现,但每个点都要经过四次。

我建议使用扫描线填充:请参阅http://lodev.org/cgtutor/floodfill.html - Scanline Floodfill Algorithm With Stack。我在我的绘图应用程序中替换了我的 4 向洪水填充,现在它更快并且在不到一秒的时间内填充了 1024x768 像素的区域。当然,速度可能会因您的实施而异。

最后,一些注意事项:

1.您可以使用CGPoint将点存储在数组中。

CGPoint point=CGPointMake(100, 50); //Declare a point and put a value in it

然后您可以使用 point.x 和 point.y 获取和设置 x 和 y 值

2.按照deanWombourne的建议,使用数组存储要检查的点。

NSMutableArray * pointsToCheck=[[NSMutableArray alloc]init];//Initialise the array
[pointsToCheck addObject:[NSValue valueWithCGPoint:start]];//Assuming you have a CGPoint start, you need to convert it to a NSValue before you put it into the array like [NSValue valueWithCGPoint:point]

while ([pointsToCheck count]>0){//While there are points to be checked:
    NSValue * pointValue=[pointsToCheck objectAtIndex:0];//Get the point: It doesn't matter if you get the first or last item
    [pointsToCheck removeObjectAtIndex:0];//Remove the point from the queue so we won't go through it again

    CGPoint point=[pointValue CGPointValue];//Get the CGPoint from the NSValue object

    //Implement your algorithm here: check for neighbour points, paint the current point or whatever. I'd recommend  using scanline fill - see above
    //When you need to add a point to the queue (If it was a recursive function, then you'd call the function from itself with different parameters) use:
    [pointsToCheck addObject:[NSValue valueWithCGPoint:newPoint];
}
于 2013-01-25T10:16:49.483 回答