洪水填充崩溃

时间:2012-01-30 05:39:26

标签: objective-c ios flood-fill

我一直试图让一个简单的Flood Fill算法适用于我正在开发的iPhone应用程序,但我无法让它正常工作。

我已经让实际的过程工作得很好但是当填充太大时应用程序会崩溃。从我可以告诉它,因为线程从所有运行的函数溢出。根据我的阅读,我需要实现一个堆栈,但我无法弄清楚它是如何工作的。

typedef struct {
int red;
int green;
int blue;
} color;

@interface EMFloodTest : UIViewController {

UIImageView *mainImage;
unsigned char *imageData;

color selColor;
color newColor;

int maxByte;
}

@end


@implementation EMFloodTest

- (void)setupImageData {
CGImageRef imageRef = mainImage.image.CGImage;
if (imageRef == NULL) { return; }
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;

maxByte = height * width * 4;
imageData = malloc(height * width * 4);

CGContextRef context = CGBitmapContextCreate(imageData, width, height, bitsPerComponent, bytesPerRow, colorSpace,
                                             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
    mainImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"Color6.png"]];
    [self.view addSubview:mainImage];
    newColor.red = 255;
    newColor.green = 94;
    newColor.blue = 0;
    [self setupImageData];
}
return self;
}

- (void)updateImage {
CGImageRef imageRef = mainImage.image.CGImage;
if (imageRef == NULL) { return; }
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;

CGContextRef context = CGBitmapContextCreate(imageData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast ); 

imageRef = CGBitmapContextCreateImage (context);
mainImage.image = [UIImage imageWithCGImage:imageRef];  

CGContextRelease(context);  
}

- (void)setPixel:(NSUInteger)byte toColor:(color)color {
imageData[byte] = color.red;
imageData[byte+1] = color.green;
imageData[byte+2] = color.blue;
}

- (BOOL)testByte:(NSInteger)byte againstColor:(color)color {
if (imageData[byte] == color.red && imageData[byte+1] == color.green && imageData[byte+2] == color.blue) {
    return YES;
} else {
    return NO;
}
}

// This is where the flood fill starts. Its a basic implementation but crashes when filling large sections.

- (void)floodFillFrom:(NSInteger)byte bytesPerRow:(NSInteger)bpr {
int u = byte - bpr;
int r = byte + 4;
int d = byte + bpr;
int l = byte - 4;
if ([self testByte:u againstColor:selColor]) {
    [self setPixel:u toColor:newColor];
    [self floodFillFrom:u bytesPerRow:bpr];
}
if ([self testByte:r againstColor:selColor]) {
    [self setPixel:r toColor:newColor];
    [self floodFillFrom:r bytesPerRow:bpr];
}
if ([self testByte:d againstColor:selColor]) {
    [self setPixel:d toColor:newColor];
    [self floodFillFrom:d bytesPerRow:bpr];
}
if ([self testByte:l againstColor:selColor]) {
    [self setPixel:l toColor:newColor];
    [self floodFillFrom:l bytesPerRow:bpr];
}
}

- (void)startFillFrom:(NSInteger)byte bytesPerRow:(NSInteger)bpr {
if (imageData[byte] == 0 && imageData[byte+1] == 0 && imageData[byte+2] == 0) {
    NSLog(@"Black Selected");
    return;
} else if ([self testByte:byte againstColor:newColor]) {
    NSLog(@"Same Fill Color");
} else {
    // code goes here
    NSLog(@"Color to be replaced");
    [self floodFrom:byte bytesPerRow:bpr];
    [self updateImage];
}
}


- (void)selectedColor:(CGPoint)point {
CGImageRef imageRef = mainImage.image.CGImage;
if (imageRef == NULL) { return; }
if (imageData == NULL) { return; }
NSInteger width = CGImageGetWidth(imageRef);
NSInteger byteNumber = 4*((width*round(point.y))+round(point.x));
NSInteger bytesPerPixel = 4;
NSInteger bytesPerRow = bytesPerPixel * width;

selColor.red = imageData[byteNumber];
selColor.green = imageData[byteNumber + 1];
selColor.blue = imageData[byteNumber + 2];
NSLog(@"Selected Color, RGB: %i, %i, %i",selColor.red, selColor.green, selColor.blue);
NSLog(@"Byte:%i",byteNumber);
[self startFillFrom:byteNumber bytesPerRow:bytesPerRow];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:mainImage];
[self selectedColor:location];
}

对于我如何能够实现堆栈甚至使用其他算法的任何帮助都将非常感激。

最佳, 达伦

1 个答案:

答案 0 :(得分:0)

问题是递归实现。

对函数进行过多的深度递归调用会导致堆栈溢出错误。

您必须以迭代方式实现算法。

如果您想查看洪水填充的迭代示例,您可以访问:
UIImageScanlineFloodfill