This repository has been archived by the owner on Mar 16, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Condition on y to solve crash issue.
- Loading branch information
1 parent
f0bad5d
commit 311cc33
Showing
7 changed files
with
292 additions
and
51 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// UIImage+FloodFill.h | ||
// ImageFloodFilleDemo | ||
// | ||
// Created by chintan on 15/07/13. | ||
// Copyright (c) 2013 ZWT. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
#import "LinkedListStack.h" | ||
|
||
@interface UIImage (FloodFill) | ||
|
||
- (UIImage *) floodFillFromPoint:(CGPoint)startPoint withColor:(UIColor *)newColor andTolerance:(int)tolerance; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
// | ||
// UIImage+FloodFill.m | ||
// ImageFloodFilleDemo | ||
// | ||
// Created by chintan on 15/07/13. | ||
// Copyright (c) 2013 ZWT. All rights reserved. | ||
// | ||
|
||
#import "UIImage+FloodFill.h" | ||
|
||
@implementation UIImage (FloodFill) | ||
/* | ||
startPoint : Point from where you want to color. Generaly this is touch point. | ||
This is important because color at start point will be replaced with other. | ||
newColor : This color will be apply at point where the match on startPoint color found. | ||
tolerance : If Tolerance is 0 than it will search for exact match of color | ||
other wise it will take range according to tolerance value. | ||
If You dont want to use tolerance and want to incress performance Than you can change | ||
compareColor(ocolor, color, tolerance) with just ocolor==color which reduse function call. | ||
*/ | ||
- (UIImage *) floodFillFromPoint:(CGPoint)startPoint withColor:(UIColor *)newColor andTolerance:(int)tolerance; | ||
{ | ||
@try | ||
{ | ||
/* | ||
First We create rowData from UIImage. | ||
We require this conversation so that we can use detail at pixel like color at pixel. | ||
You can get some discussion about this topic here: | ||
http://stackoverflow.com/questions/448125/how-to-get-pixel-data-from-a-uiimage-cocoa-touch-or-cgimage-core-graphics | ||
*/ | ||
|
||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | ||
|
||
CGImageRef imageRef = [self CGImage]; | ||
|
||
NSUInteger width = CGImageGetWidth(imageRef); | ||
NSUInteger height = CGImageGetHeight(imageRef); | ||
|
||
unsigned char *imageData = malloc(height * width * 4); | ||
|
||
NSUInteger bytesPerPixel = CGImageGetBitsPerPixel(imageRef) / 8; | ||
NSUInteger bytesPerRow = CGImageGetBytesPerRow(imageRef); | ||
NSUInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef); | ||
|
||
CGContextRef context = CGBitmapContextCreate(imageData, | ||
width, | ||
height, | ||
bitsPerComponent, | ||
bytesPerRow, | ||
colorSpace, | ||
CGImageGetBitmapInfo(imageRef)); | ||
CGColorSpaceRelease(colorSpace); | ||
|
||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); | ||
|
||
//Get color at start point | ||
unsigned int byteIndex = (bytesPerRow * startPoint.y) + startPoint.x * bytesPerPixel; | ||
|
||
unsigned int ocolor = getColorCode(byteIndex, imageData); | ||
|
||
//Convert newColor to RGBA value so we can save it to image. | ||
int newRed, newGreen, newBlue, newAlpha; | ||
|
||
const CGFloat *components = CGColorGetComponents(newColor.CGColor); | ||
|
||
/* | ||
If you are not getting why I use CGColorGetNumberOfComponents than read following link: | ||
http://stackoverflow.com/questions/9238743/is-there-an-issue-with-cgcolorgetcomponents | ||
*/ | ||
|
||
if(CGColorGetNumberOfComponents(newColor.CGColor) == 2) | ||
{ | ||
newRed = newGreen = newBlue = components[0] * 255; | ||
newAlpha = components[1]; | ||
} | ||
else if (CGColorGetNumberOfComponents(newColor.CGColor) == 4) | ||
{ | ||
newRed = components[0] * 255; | ||
newGreen = components[1] * 255; | ||
newBlue = components[2] * 255; | ||
newAlpha = 255; | ||
} | ||
|
||
unsigned int ncolor = (newRed << 24) | (newGreen << 16) | (newBlue << 8) | newAlpha; | ||
|
||
/* | ||
We are using stack to store point. | ||
Stack is implemented by LinkList. | ||
To incress speed I have used NSMutableData insted of NSMutableArray. | ||
To see Detail Of This implementation visit following leink. | ||
http://iwantmyreal.name/blog/2012/09/29/a-faster-array-in-objective-c/ | ||
*/ | ||
|
||
LinkedListStack *points = [[LinkedListStack alloc] initWithCapacity:500 incrementSize:500 andMultiplier:height]; | ||
|
||
int x = startPoint.x; | ||
int y = startPoint.y; | ||
|
||
[points pushFrontX:x andY:y]; | ||
|
||
/* | ||
This algorithem is prety simple though it llook odd in Objective C syntex. | ||
To get familer with this algorithm visit following link. | ||
http://lodev.org/cgtutor/floodfill.html | ||
You can read hole artical for knowledge. | ||
If you are familer with flood fill than got to Scanline Floodfill Algorithm With Stack (floodFillScanlineStack) | ||
*/ | ||
|
||
unsigned int color; | ||
BOOL spanLeft,spanRight; | ||
|
||
while ([points popFront:&x andY:&y] != INVALID_NODE_CONTENT) | ||
{ | ||
byteIndex = (bytesPerRow * y) + x * bytesPerPixel; | ||
|
||
color = getColorCode(byteIndex, imageData); | ||
|
||
while(y >= 0 && compareColor(ocolor, color, tolerance)) | ||
{ | ||
y--; | ||
|
||
if(y >= 0) | ||
{ | ||
byteIndex = (bytesPerRow * y) + x * bytesPerPixel; | ||
|
||
color = getColorCode(byteIndex, imageData); | ||
} | ||
} | ||
|
||
y++; | ||
|
||
spanLeft = spanRight = NO; | ||
|
||
byteIndex = (bytesPerRow * y) + x * bytesPerPixel; | ||
|
||
color = getColorCode(byteIndex, imageData); | ||
|
||
while (y < height && compareColor(ocolor, color, tolerance) && ncolor != color) | ||
{ | ||
//Change old color with newColor RGBA value | ||
imageData[byteIndex + 0] = newRed; | ||
imageData[byteIndex + 1] = newGreen; | ||
imageData[byteIndex + 2] = newBlue; | ||
imageData[byteIndex + 3] = newAlpha; | ||
|
||
if(x > 0) | ||
{ | ||
byteIndex = (bytesPerRow * y) + (x - 1) * bytesPerPixel; | ||
|
||
color = getColorCode(byteIndex, imageData); | ||
|
||
if(!spanLeft && x > 0 && compareColor(ocolor, color, tolerance)) | ||
{ | ||
[points pushFrontX:(x - 1) andY:y]; | ||
|
||
spanLeft = YES; | ||
} | ||
else if(spanLeft && x > 0 && !compareColor(ocolor, color, tolerance)) | ||
{ | ||
spanLeft = NO; | ||
} | ||
} | ||
|
||
if(x < width - 1) | ||
{ | ||
byteIndex = (bytesPerRow * y) + (x + 1) * bytesPerPixel; | ||
|
||
color = getColorCode(byteIndex, imageData); | ||
|
||
if(!spanRight && compareColor(ocolor, color, tolerance)) | ||
{ | ||
[points pushFrontX:(x + 1) andY:y]; | ||
|
||
spanRight = YES; | ||
} | ||
else if(spanRight && !compareColor(ocolor, color, tolerance)) | ||
{ | ||
spanRight = NO; | ||
} | ||
} | ||
|
||
y++; | ||
|
||
if(y < height) | ||
{ | ||
byteIndex = (bytesPerRow * y) + x * bytesPerPixel; | ||
|
||
color = getColorCode(byteIndex, imageData); | ||
} | ||
} | ||
} | ||
|
||
//Convert Flood filled image row data back to UIImage object. | ||
|
||
CGImageRef newCGImage = CGBitmapContextCreateImage(context); | ||
|
||
UIImage *result = [UIImage imageWithCGImage:newCGImage]; | ||
|
||
CGImageRelease(newCGImage); | ||
|
||
CGContextRelease(context); | ||
|
||
free(imageData); | ||
|
||
return result; | ||
} | ||
@catch (NSException *exception) | ||
{ | ||
NSLog(@"Exception : %@", exception); | ||
} | ||
} | ||
|
||
/* | ||
I have used pure C function because it is said than C function is faster than Objective - C method in call. | ||
This two function are called most of time so it require that calling this work in speed. | ||
I have not verified this performance so I like to here comment on this. | ||
*/ | ||
/* | ||
This function extract color from image and convert it to integer represent. | ||
Converting to integer make comperation easy. | ||
*/ | ||
unsigned int getColorCode (unsigned int byteIndex, unsigned char *imageData) | ||
{ | ||
unsigned int red = imageData[byteIndex]; | ||
unsigned int green = imageData[byteIndex + 1]; | ||
unsigned int blue = imageData[byteIndex + 2]; | ||
unsigned int alpha = imageData[byteIndex + 3]; | ||
|
||
return (red << 24) | (green << 16) | (blue << 8) | alpha; | ||
} | ||
|
||
/* | ||
This function compare two color with counting tolerance value. | ||
If color is between tolerance rancge than it return true other wise false. | ||
*/ | ||
bool compareColor (unsigned int color1, unsigned int color2, int tolorance) | ||
{ | ||
if(color1 == color2) | ||
return true; | ||
|
||
int red1 = ((0xff000000 & color1) >> 24); | ||
int green1 = ((0x00ff0000 & color1) >> 16); | ||
int blue1 = ((0x0000ff00 & color1) >> 8); | ||
int alpha1 = (0x000000ff & color1); | ||
|
||
int red2 = ((0xff000000 & color2) >> 24); | ||
int green2 = ((0x00ff0000 & color2) >> 16); | ||
int blue2 = ((0x0000ff00 & color2) >> 8); | ||
int alpha2 = (0x000000ff & color2); | ||
|
||
int diffRed = abs(red2 - red1); | ||
int diffGreen = abs(green2 - green1); | ||
int diffBlue = abs(blue2 - blue1); | ||
int diffAlpha = abs(alpha2 - alpha1); | ||
|
||
if( diffRed > tolorance || | ||
diffGreen > tolorance || | ||
diffBlue > tolorance || | ||
diffAlpha > tolorance ) | ||
{ | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
@end |
Binary file modified
BIN
-867 Bytes
(98%)
...xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters