Getting UIImage for only particular area bounds drawn - PaintView - ios

I have already implemented paint / draw using:
- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
-(void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
Now issue is that for any line drawn, I want to get that particular line / paint image. I don't want image of entire screen, only area / bounds of line / paint drawn.
Reason is that I want to perform pan gesture / delete functionality on that line / paint drawn.
User can draw multiple lines, so want UIImage for all this lines separately.
Any logic or code snippet will be really helpful
Thanks in advance

Depending on your application, particularly how many times you plan on doing this in a row, you may be able to create a different image/layer for each paint line. Your final image would essentially be all the individual lines drawn on top of each other.
It may be more efficient to create a custom view to capture touch events. You could store the list of touch coordinates for each paint line and render them all at once in a custom drawRect. This way you are storing lists of coordinates for each paint line, and can still access each one, instead of a list of images. You could calculate the area/bounds from the coordinates used to render the line.
Additional context and code may be helpful, I'm not sure I completely understand what you're trying to accomplish!

I take a look at the MVPaint project. It seems you have an object:
MVPaintDrawing _drawing;
which contains an array of MVPaintTransaction. You can iterate on those MVPaintTransaction to draw an UIImage.
So first you can add a method to get an image from a MVPaintTransaction:
- (UIImage *) imageToDrawWithSize:(CGSize) size xScale:(CGFloat)xScale yScale:(CGFloat)yScale {
UIGraphicsBeginImageContext(size);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), xScale, yScale);
// call the existing draw method
[self draw];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
Then add a method to get an array of image from the array of MVPaintTransaction in the MVPaintDrawing class:
- (NSArray *) getImagesFromDrawingOnSurface: (UIImageView *) surface xScale: (CGFloat) xScale yScale: (CGFloat) yScale{
NSMutableArray *imageArray = [NSMutableArray new];
for (MVPaintTransaction * transaction in _drawing) {
UIImage *image = [transaction imageToDrawWithSize:surface.frame.size xScale:xScale yScale:yScale];
[imageArray addObject:image];
}
return imageArray;
}
In this way you will have an array of UIImage corresponding to each line you have drawn. If you want those images to have the "minimum" possible size (i mean without extra alpha part), you can apply this method (I added it in the MVPaintTransaction class):
- (UIImage *)trimmedImage:(UIImage *)img {
CGImageRef inImage = img.CGImage;
CFDataRef m_DataRef;
m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef);
size_t width = CGImageGetWidth(inImage);
size_t height = CGImageGetHeight(inImage);
CGPoint top,left,right,bottom;
BOOL breakOut = NO;
for (int x = 0;breakOut==NO && x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
left = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int y = 0;breakOut==NO && y < height; y++) {
for (int x = 0; x < width; x++) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
top = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int y = height-1;breakOut==NO && y >= 0; y--) {
for (int x = width-1; x >= 0; x--) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
bottom = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int x = width-1;breakOut==NO && x >= 0; x--) {
for (int y = height-1; y >= 0; y--) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
right = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
CGFloat scale = img.scale;
CGRect cropRect = CGRectMake(left.x / scale, top.y/scale, (right.x - left.x)/scale, (bottom.y - top.y) / scale);
UIGraphicsBeginImageContextWithOptions( cropRect.size,
NO,
scale);
[img drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y)
blendMode:kCGBlendModeCopy
alpha:1.];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CFRelease(m_DataRef);
return croppedImage;
}
Then simply replace in the first method:
return result;
by
return [self trimmedImage:result];

Related

Vertically scrolling background - Sprite Kit

I'm working on a game that is in portrait mode and can't seem to get this background to scroll smoothly on the y-axis. It doesn't properly add a second background so there is always a gap between the 2 backgrounds.
Heres the code:
static const float BG_VELOCITY = 100.0;
static inline CGPoint CGPointAdd(const CGPoint a, const CGPoint b)
{
return CGPointMake(a.x + b.x, a.y + b.y);
}
static inline CGPoint CGPointMultiplyScalar(const CGPoint a, const CGFloat b)
{
return CGPointMake(a.x * b, a.y * b);
}
Methods:
-(void)initalizingScrollingBackground
{
for (int i = 0; i < 2; i++) {
SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:#"bg"];
bg.zRotation = M_PI_2;
bg.position = CGPointMake(320, self.frame.origin.y);
bg.anchorPoint = CGPointZero;
bg.name = #"bg";
[self addChild:bg];
}
}
- (void)moveBg
{
[self enumerateChildNodesWithName:#"bg" usingBlock: ^(SKNode *node, BOOL *stop)
{
SKSpriteNode * bg = (SKSpriteNode *) node;
CGPoint bgVelocity = CGPointMake(0, -BG_VELOCITY);
CGPoint amtToMove = CGPointMultiplyScalar(bgVelocity,_dt);
bg.position = CGPointAdd(bg.position, amtToMove);
//Checks if bg node is completely scrolled of the screen, if yes then put it at the end of the other node
if (bg.position.y <= -bg.size.height)
{
bg.position = CGPointMake(bg.position.x,
bg.position.y + bg.size.height*2);
}
}];
}
What am I doing wrong that it has a gap in between the two backgrounds. Note: Background size is 568 x 320 if that is necessary info.
Thanks.
There a few problems. The biggest is the fact that your image is made in landscape mode. Simply edit the image in photoshop or preview to rotate it 90 degrees, then take out the zRotation. This will work for your height dilemma. Then change bg.position = CGPointMake(320,... etc to bg.position = CGPointMake (0, i * bg.size.height);
Also change your if statement to:
if (bg.position.y <= -bg.size.height)
{
bg.position = CGPointMake(0, bg.position.y + bg.size.height*2);
}
And your code will work.

Intersection of CGRect and CGPath

Is there an efficient way to detect if a CGPath and a CGRect intersect?
I've thought about looping through every point inside the CGRect sort of like this:
for (CGPoint point in rect) {
if (CGPathContainsPoint(path, nil, point, NO)) {
intersects = YES;
break;
}
But I wanted to know if there was a better way.
In my app there are many CGrects and one CGPath that may change its shape. I just need to check which rects are intersecting with the path, as shown in the image bellow.
Draw the path in a bitmap (white on alpha)
then check the Rectangle part of bitmap. check if there is any white in that area which would mean overlapping
-- to make it more performant draw only the part of the bitmap that is in the rectangle.
I havent tried this and it wont offer real-time performance but it sounds ok to me
The only completely accurate way that I've found of doing this is to loop through every point, something like this (I haven't tested this exact code):
for (NSValue *rectValue in rects) {
CGRect rect = rectValue.CGRectValue;
// Set the initial point values to the top left of the current rect
CGFloat x = CGRectGetMinX(rect);
CGFloat y = CGRectGetMinY(rect);
BOOL intersects = NO;
BOOL morePoints = YES;
// Loop until there are no more points to check in this rect
while (morePoints) {
CGPoint point = CGPointMake(x, y);
if (CGPathContainsPoint(path, nil, point, NO)) {
intersects = YES;
break;
}
// Adjust the x and y values to check all points
if (x < CGRectGetMaxX(rect)) {
x++;
} else if (y < CGRectGetMaxY(rect)) {
x = CGRectGetMinX(rect);
y++;
} else {
morePoints = NO;
}
}
if (intersects) {
// The path intersects the current rect
} else {
// The path does not intersect the current rect
}
}
But this is the most inefficient way.
You could optimize this solution by looping asynchronously:
[rects enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSValue * _Nonnull rectValue, NSUInteger idx, BOOL * _Nonnull stop) {
CGRect rect = rectValue.CGRectValue;
...
}];
I only needed to know if the path intersected the edge of a rect so I did something similar to this:
for (NSValue *rectValue in rects) {
CGRect rect = rectValue.CGRectValue;
// Set the initial point values to the top left of the current rect
CGFloat x = CGRectGetMinX(rect);
CGFloat y = CGRectGetMinY(rect);
BOOL intersects = NO;
// top edge
for (; x < CGRectGetMaxX(rect); x++) {
CGPoint point = CGPointMake(x, y);
if (CGPathContainsPoint(path, nil, point, NO)) {
intersects = YES;
break;
}
}
if (intersects) {
// The path intersects the current rect on the top edge
}
// right edge
for (; y < CGRectGetMaxY(rect); y++) {
CGPoint point = CGPointMake(x, y);
if (CGPathContainsPoint(path, nil, point, NO)) {
intersects = YES;
break;
}
}
if (intersects) {
// The path intersects the current rect on the right edge
}
// bottom edge
x = CGRectGetMinX(rect);
for (; x < CGRectGetMaxX(rect); x++) {
CGPoint point = CGPointMake(x, y);
if (CGPathContainsPoint(path, nil, point, NO)) {
intersects = YES;
break;
}
}
if (intersects) {
// The path intersects the current rect on the bottom edge
}
// left edge
x = CGRectGetMinX(rect);
y = CGRectGetMinY(rect);
for (; y < CGRectGetMaxY(rect); y++) {
CGPoint point = CGPointMake(x, y);
if (CGPathContainsPoint(path, nil, point, NO)) {
intersects = YES;
break;
}
}
if (intersects) {
// The path intersects the current rect on the left edge
}
}

How can i draw Two line with some angle between them, later can change angle by dragging any line

I want to make UBersense like app (http://blog.ubersense.com/2013/01/03/how-to-use-the-drawing-tools-in-ubersense/), there i need to draw two line with some angle, after that i can adjust the angle between two line by dragging any line or intersection point.
can you guys please provide me some idea or code snippet.
screenshots url:
https://picasaweb.google.com/yunusm7/AppScreenshots#slideshow/5952787957718627714
Thanks in advance.
You have a construction with three points, one point is an angle point, and two others are just vertices. First of all you should create a new class like this:
#interface MyAngle : NSObject {
}
#property (nonatomic) CGPoint p1;
#property (nonatomic) CGPoint p2;
#property (nonatomic) CGPoint v; // this is an angle point
#end
You can use the default implementation of this without any tricks with such sample init:
- (id)init {
if (self = [super init]) {
p1 = CGPointMake(1,0);
p2 = CGPointMake(0,1);
v = CGPointZero;
}
return self;
}
But also as I understood you need to know the value of the angle. You can do this using the following way:
- (CGFloat)valueOfAngle {
CGPoint v1 = CGPointMake(p1.x-v.x, p1.y-v.y);
CGPoint v2 = CGPointMake(p2.x-v.x, p2.y-v.y);
CGFloat scalarProduct = v1.x*v2.x + v1.y*v2.y;
CGFloat lengthProduct = sqrt(v1.x*v1.x + v1.y*v1.y)*sqrt(v2.x*v2.x + v2.y*v2.y);
CGFloat fraction = scalarProduct / lengthProduct;
if (fraction < -1) fraction = -1;
if (fraction > 1) fraction = 1;
return acos(fraction);
}
If you want to obtain angles more than 180 degrees you should change the code above a little. But there are too much information about how to do this in the Internet, so I will skip this part.
Then you need to create an instance of MyAngle in your viewController. Let it be called "angle". Knowing coordinates of every three points if enough do draw it (!!!). Implement drawRect method in a view that will contain the MyAngle instance (I strongly recommend do to this on your own subclass of UIView):
- (void)drawRect {
[super drawRect];
// set options of drawing
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
CGContextSetLineWidth(c, 3.0);
CGContextSetStrokeColor(c, red);
// draw an angle directly
CGContextBeginPath(c);
CGContextMoveToPoint(c, angle.p1.x, angle.p1.y);
CGContextAddLineToPoint(c, angle.v.x, angle.v.y);
CGContextAddLineToPoint(c, angle.p2.x, angle.p2.y);
CGContextStrokePath(c);
// draw circles around vertices (like on the screenshot you provided)
CGFloat R = 7.0f;
CGContextBeginPath(c);
CGContextAddEllipseInRect(c, CGRectMake(angle.p1.x - R, angle.p1.y - R, 2*R, 2*R));
CGContextStrokePath(c);
CGContextBeginPath(c);
CGContextAddEllipseInRect(c, CGRectMake(angle.p2.x - R, angle.p2.y - R, 2*R, 2*R));
CGContextStrokePath(c);
CGContextBeginPath(c);
CGContextAddEllipseInRect(c, CGRectMake(angle.v.x - R, angle.v.y - R, 2*R, 2*R));
CGContextStrokePath(c);
}
And that's all you need to know for drawing what you want! You can change the stroke color or radius of three circles if you want.
Then you need to have a possibility to change the locations of your angle's points. For this you can just implement panGestureRecognizer in your viewController's viewDidLoad method like this:
UIPanGestureRecognizer *pan = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveAngle:)] autorelease];
pan.delegate = self;
[self.view addGestureRecognizer:pan];
Implement UIGestureRecognizerDelegate method:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint p = [gestureRecognizer locationInView:self.view];
CGFloat d1 = sqrt((p.x-angle.p1.x)*(p.x-angle.p1.x) + (p.y-angle.p1.y)*(p.y-angle.p1.y);
CGFloat d2 = sqrt((p.x-angle.p2.x)*(p.x-angle.p2.x) + (p.y-angle.p2.y)*(p.y-angle.p2.y);
CGFloat d3 = sqrt((p.x-angle.v.x)*(p.x-angle.v.x) + (p.y-angle.v.y)*(p.y-angle.v.y);
// just check if we touched the screen near some of angle's points
CGFloat tolerance = 15.0f;
return (d1 < tolerance) || (d2 < tolerance) || (d3 < tolerance);
}
return YES;
}
and tagret's selector (also in your viewController):
- (void)moveAngle:(UIPanGestureRecognizer *)gr {
CGPoint p = [gr locationInView:self.view];
if (gr.state == UIGestureRecognizerStateBegan) {
CGFloat d1 = sqrt((p.x-angle.p1.x)*(p.x-angle.p1.x) + (p.y-angle.p1.y)*(p.y-angle.p1.y);
CGFloat d2 = sqrt((p.x-angle.p2.x)*(p.x-angle.p2.x) + (p.y-angle.p2.y)*(p.y-angle.p2.y);
CGFloat d3 = sqrt((p.x-angle.v.x)*(p.x-angle.v.x) + (p.y-angle.v.y)*(p.y-angle.v.y);
// pointToMove is your int variable
CGFloat tolerance = 15.0f;
if (d1 < tolerance) {
pointToMove = 1;
}
else if (d2 < tolerance) {
pointToMove = 2;
}
else {
pointToMove = 3;
}
}
else {
if (pointToMove == 1) {
angle.p1 = loc;
}
else if (pointToMove == 2) {
angle.p2 = loc;
}
else {
angle.v = loc;
}
[yourCustomView setNeedsDisplay];
[yourLabel setText:[NSString stringWithFormat:#"%.3f", [angle valueOfangle]*180/PI]];
}
}
Maybe I skip some evident things, but I think it should be enough for you to begin writing some code.

Cocos2D & ccDrawLine - Drawing smooth lines

I have some troubles when I try to draw lines with cocos2d! I store points, got from touchMoved method, in a NSMutableArray, and pass that array to a subclass of CCNode called Lines that I use to draw the lines from the array of points. The problem is that the line is not smooth when I swipe slowly, but when I swipe faster, the line is way much smoother. See the pictures below :
Slow Swipe :
Fast Swipe :
I tried to solve the problem with ccpDistance, which calculate the distance between the last saved point and if it's not far enough I don't save it. I also tried to draw little circles at each saved positions, but this isn't really nice neither. Here's my code :
In my GameScene :
- (void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
if (ccpDistance(lastPoint, location) > 10) {
//SAVE THE POINT
[linePoints addObject:[NSValue valueWithCGPoint:location]];
[line updatePoints:linePoints];
lastPoint = location;
}
}
And my Line Class :
- (void) updatePoints:(NSMutableArray *)_point
{
points = _point;
}
- (void) draw
{
if ([points count] > 0) {
ccGLEnable(GL_LINE_STRIP);
ccDrawColor4B(209, 75, 75, 255);
float lineWidth = 6.0 * CC_CONTENT_SCALE_FACTOR();
glLineWidth(lineWidth);
int count = [points count];
for (int i = 0; i < (count - 1); i++){
CGPoint pos1 = [[points objectAtIndex:i] CGPointValue];
CGPoint pos2 = [[points objectAtIndex:i+1] CGPointValue];
ccDrawLine(pos1, pos2);
ccDrawSolidCircle(pos2, 2.5, 20);
}
}
}
Also, is there something in my code that could be done better to improve performance? Right now I don't have any problems even with 1000+ points, but just in case...
Any help will be greatly appreciated! Thanks in advance!
Ok, I found a website explaining really clearly how to do smooth lines, and it worked wonderfully! There's still anti-aliasing that's left to do, but maybe I will never do, since it looks really great on retina devices. Here's the website : Drawing Smooth Lines with Cocos2D
And here's the result :
Also, for those interested in the finished code, here it is :
Line.m
- (void) drawCurPoint:(CGPoint)curPoint PrevPoint:(CGPoint)prevPoint
{
float lineWidth = 6.0;
ccColor4F red = ccc4f(209.0/255.0, 75.0/255.0, 75.0/255.0, 1.0);
//These lines will calculate 4 new points, depending on the width of the line and the saved points
CGPoint dir = ccpSub(curPoint, prevPoint);
CGPoint perpendicular = ccpNormalize(ccpPerp(dir));
CGPoint A = ccpAdd(prevPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint B = ccpSub(prevPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint C = ccpAdd(curPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint D = ccpSub(curPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint poly[4] = {A, C, D, B};
//Then draw the poly, and a circle at the curPoint to get smooth corners
ccDrawSolidPoly(poly, 4, red);
ccDrawSolidCircle(curPoint, lineWidth/2.0, 20);
}
- (void) draw
{
if ([points count] > 0) {
ccGLEnable(GL_LINE_STRIP);
ccColor4F red = ccc4f(209.0/255.0, 75.0/255.0, 75.0/255.0, 1.0);
ccDrawColor4F(red.r, red.g, red.b, red.a);
float lineWidth = 6.0 * CC_CONTENT_SCALE_FACTOR();
glLineWidth(lineWidth);
int count = [points count];
for (int i = 0; i < (count - 1); i++){
CGPoint pos1 = [[points objectAtIndex:i] CGPointValue];
CGPoint pos2 = [[points objectAtIndex:i+1] CGPointValue];
[self drawCurPoint:pos2 PrevPoint:pos1];
}
}
}
As for the GameScene, nothing changed there (See the question for the code)! Note that you can change the line if (ccpDistance(lastPoint, location) > X), where X is the minimum distance between two points before the game saves another one. The lower X is, the smoother the line will be, but you will have way more points in your array, which could affect performance!
Anyway, thank you guys for your suggestions and your help, it helped me to get in the right way!
I think that you could smooth up your line drawing with some averaging.
- (void) updatePoints:(NSMutableArray *)_point
{
points = _point;
int count = [points count];
for (int i = 3; i < (count - 4); i++) {
CGPoint pos1 = [[points objectAtIndex:i - 2] CGPointValue];
CGPoint pos2 = [[points objectAtIndex:i - 1] CGPointValue];
CGPoint pos3 = [[points objectAtIndex:i] CGPointValue];
CGPoint pos4 = [[points objectAtIndex:i + 1] CGPointValue];
CGPoint pos5 = [[points objectAtIndex:i + 2] CGPointValue];
CGFloat xpos = (pos1.x + pos2.x + 2 * pos3.x + pos4.x + pos5.x)/6;
...
(now calcuclate ypos similarly and store the point into an array)
}
}

Crop UIImage to alpha

I have a rather large, almost full screen image that I'm going to be displaying on an iPad. The image is about 80% transparent. I need to, on the client, determine the bounding box of the opaque pixels, and then crop to that bounding box.
Scanning other questions here on StackOverflow and reading some of the CoreGraphics docs, I think I could accomplish this by:
CGBitmapContextCreate(...) // Use this to render the image to a byte array
..
- iterate through this byte array to find the bounding box
..
CGImageCreateWithImageInRect(image, boundingRect);
That just seems very inefficient and clunky. Is there something clever I can do with CGImage masks or something which makes use of the device's graphics acceleration to do this?
Thanks to user404709 for making all the hard work.
Below code also handles retina images and frees the CFDataRef.
- (UIImage *)trimmedImage {
CGImageRef inImage = self.CGImage;
CFDataRef m_DataRef;
m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef);
size_t width = CGImageGetWidth(inImage);
size_t height = CGImageGetHeight(inImage);
CGPoint top,left,right,bottom;
BOOL breakOut = NO;
for (int x = 0;breakOut==NO && x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
left = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int y = 0;breakOut==NO && y < height; y++) {
for (int x = 0; x < width; x++) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
top = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int y = height-1;breakOut==NO && y >= 0; y--) {
for (int x = width-1; x >= 0; x--) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
bottom = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int x = width-1;breakOut==NO && x >= 0; x--) {
for (int y = height-1; y >= 0; y--) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
right = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
CGFloat scale = self.scale;
CGRect cropRect = CGRectMake(left.x / scale, top.y/scale, (right.x - left.x)/scale, (bottom.y - top.y) / scale);
UIGraphicsBeginImageContextWithOptions( cropRect.size,
NO,
scale);
[self drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y)
blendMode:kCGBlendModeCopy
alpha:1.];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CFRelease(m_DataRef);
return croppedImage;
}
I created a category on UImage which does this if any one needs it...
+ (UIImage *)cropTransparencyFromImage:(UIImage *)img {
CGImageRef inImage = img.CGImage;
CFDataRef m_DataRef;
m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage));
UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef);
int width = img.size.width;
int height = img.size.height;
CGPoint top,left,right,bottom;
BOOL breakOut = NO;
for (int x = 0;breakOut==NO && x < width; x++) {
for (int y = 0; y < height; y++) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
left = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int y = 0;breakOut==NO && y < height; y++) {
for (int x = 0; x < width; x++) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
top = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int y = height-1;breakOut==NO && y >= 0; y--) {
for (int x = width-1; x >= 0; x--) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
bottom = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
breakOut = NO;
for (int x = width-1;breakOut==NO && x >= 0; x--) {
for (int y = height-1; y >= 0; y--) {
int loc = x + (y * width);
loc *= 4;
if (m_PixelBuf[loc + 3] != 0) {
right = CGPointMake(x, y);
breakOut = YES;
break;
}
}
}
CGRect cropRect = CGRectMake(left.x, top.y, right.x - left.x, bottom.y - top.y);
UIGraphicsBeginImageContextWithOptions( cropRect.size,
NO,
0.);
[img drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y)
blendMode:kCGBlendModeCopy
alpha:1.];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage;
}
There is no clever cheat to get around having the device do the work, but there are some ways to accelerate the task, or minimize the impact on the user interface.
First, consider the need to accelerate this task. A simple iteration through this byte array may go fast enough. There may be no need to invest in optimizing this task if the app is just calculating this once per run or in reaction to a user's choice that takes at least a few seconds between choices.
If the bounding box is not needed for some time after the image becomes available, this iteration may be launched in a separate thread. That way the calculation doesn't block the main interface thread. Grand Central Dispatch may make using a separate thread for this task easier.
If the task must be accelerated, maybe this is real time processing of video images, then parallel processing of the data may help. The Accelerate framework may help in setting up SIMD calculations on the data. Or, to really get performance with this iteration, ARM assembly language code using the NEON SIMD operations could get great results with significant development effort.
The last choice is to investigate a better algorithm. There's a huge body of work on detecting features in images. An edge detection algorithm may be faster than a simple iteration through the byte array. Maybe Apple will add edge detection capabilities to Core Graphics in the future which can be applied to this case. An Apple implemented image processing capability may not be an exact match for this case, but Apple's implementation should be optimized to use the SIMD or GPU capabilities of the iPad, resulting in better overall performance.

Resources