2013-03-15 68 views
3

我正在绘制应用程序并实施填充填充算法。 下面是我实现的代码: https://github.com/OgreSwamp/ObjFloodFill/blob/master/src/FloodFill.miPhone/iPad上的填充性能问题

和viewController.h文件

#import <UIKit/UIKit.h> 
#import "AppDelegate.h" 

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


@interface ViewController : UIViewController 
{ 
    AppDelegate *appDelegate; 
    UIImageView *mainImage; 
    UIView *loadingView; 
    unsigned char *imageData; 
    UIActivityIndicatorView *activityIndicator; 
    color selColor; 
    color newColor; 
    BOOL boolVariable; 
    int maxByte; 

} 
- (IBAction)fn_btnRed:(id)sender; 
- (IBAction)fn_btnGreen:(id)sender; 
- (IBAction)fn_btnBlue:(id)sender; 
- (IBAction)fn_btnSave:(id)sender; 
-(void)alertActivityClose; 
@end 

Viewcontroller.m文件

#import "ViewController.h" 
#import "FloodFill.h" 

@implementation ViewController 


#pragma mark - View lifecycle 
- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Release any cached data, images, etc that aren't in use. 
} 


- (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:@"scaledWhite.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); 
    if (boolVariable==YES) 
    { 
     UIImageWriteToSavedPhotosAlbum(mainImage.image, nil, nil, nil); 
    } 
    boolVariable=NO; 
    } 

- (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)shiftingOnMainThread 
    { 
    [self performSelectorOnMainThread:@selector(updateImage) withObject:nil waitUntilDone:YES]; 
    } 

- (void)startFillFrom:(NSInteger)byte bytesPerRow:(NSInteger)bpr 
{ 
    if (imageData[byte] == 0 && imageData[byte+1] == 0 && imageData[byte+2] == 0) 
    { 
    return; 
    } 
    else if ([self testByte:byte againstColor:newColor]) 
    { 
    NSLog(@"Same Fill Color"); 
    } 
    else 
    { 
    // code goes here 
    NSLog(@"Color to be replaced"); 
    [self floodFillFrom: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)); 
    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); 
} 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UITouch *touch = [touches anyObject]; 
    CGPoint location = [touch locationInView:mainImage]; 
    [self selectedColor:location]; 
    CGImageRef imageRef = mainImage.image.CGImage; 
    NSInteger width = CGImageGetWidth(imageRef); 
    NSInteger height = CGImageGetHeight(imageRef); 

    int x = 0; 
    x |= (selColor.red & 0xff) << 24; 
    x |= (selColor.green & 0xff) << 16; 
    x |= (selColor.blue & 0xff) << 8; 
    x |= (selColor.alpha & 0xff); 

    int y = 0; 
    y |= (newColor.red & 0xff) << 24; 
    y |= (newColor.green & 0xff) << 16; 
    y |= (newColor.blue & 0xff) << 8; 
    y |= (newColor.alpha & 0xff); 


    [NSThread detachNewThreadSelector:@selector(shiftingOnMainThread) toTarget:self withObject:nil]; 
} 
-(void)alertActivityClose 
{ 
    [activityIndicator stopAnimating]; 
    [activityIndicator hidesWhenStopped]; 
    loadingView.hidden=YES; 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
// Do any additional setup after loading the view, typically from a nib. 
} 

- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 
// Release any retained subviews of the main view. 
// e.g. self.myOutlet = nil; 
    } 

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 
} 

- (void)viewDidAppear:(BOOL)animated 
{ 
    [super viewDidAppear:animated]; 
} 

- (void)viewWillDisappear:(BOOL)animated 
{ 
[super viewWillDisappear:animated]; 
} 

- (void)viewDidDisappear:(BOOL)animated 
{ 
[super viewDidDisappear:animated]; 
    } 

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
    { 
    // Return YES for supported orientations 
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); 
    } 

    - (IBAction)fn_btnRed:(id)sender 
    { 
    newColor.red = 255; 
    newColor.green = 0; 
    newColor.blue = 0; 

    } 

    - (IBAction)fn_btnGreen:(id)sender 
    { 
    newColor.red = 0; 
    newColor.green = 255; 
    newColor.blue = 0; 

    } 

- (IBAction)fn_btnBlue:(id)sender 
{ 
    newColor.red = 0; 
    newColor.green = 0; 
    newColor.blue = 255; 

} 

- (IBAction)fn_btnSave:(id)sender 
{ 
    boolVariable=YES; 
    [self updateImage]; 

} 
    @end 

它给人的性能问题,如果用户继续使用应用程序和大约需要20-30秒才能填满一小部分,同时也会导致内存错误。

有没有人有这样的问题与洪水填补和如何解决它?

+0

我认为这是内存管理的问题。注意物体的释放和分配 – user247 2013-03-15 08:03:50

+0

您是否尝试使用ARC来处理该特定文件。 – skyline 2013-03-15 08:53:40

+0

我为所有文件使用ARC。 – Bond 2013-03-15 09:15:53

回答

0

崩溃明智:我没有看到任何代码阻止填充环绕图像的左/右端或像素数据的顶部或底部。不限制左/右洪水可能会带来意想不到的结果。超出顶部/底部很可能导致非法内存访问(崩溃)。

颜色结构应该可能被定义为四个uint8_t(不是int)。我还将imageData定义为指向此(属性大小)结构的指针,并让编译器处理.red,.green组件,而不是硬编码+0,+ 1,+2偏移量。

几乎在任何你硬编码(假设)的值我会使用“sizeof(x)”来代替。

NSUInteger bytesPerPixel = sizeof(color); 
0

明智的性能,你与指标玩耍,迫使上的绝对地址,几乎每个像素获得的重新计算(因为实际像素访问到(递归)子程序的下一次调用)。直接使用像素地址通常要快得多(特别是对于水平(左侧< - >右侧))。

我不知道多少开销ObjC方法调度添加,但我会考虑重写floodFillFrom:bytesPerRow:,testByte:againstColor:和setPixel:toColor:作为strait C函数。 (测试&设置为静态内联。)