2013-03-15 63 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函數。 (測試&設置爲靜態內聯。)