JigSaw puzzle uiimage irregular cropping - objective-c

Am developing jigsaw puzzle for iphone.
Here using the masking technique I have cropped the image into 9 peices. See the image below.
After cropping some portion of image is missing due to masking. I knew this is coz of loading those cropped images in square uiimageview.
My question is how to make it as complete cropped image without losing any portion of image and how to fit these pieces correctly to match with original one.

Build a set of masks corresponding to to each puzzle piece. Each mask should be the size of the original image and all black except for a white area with the position and shape of the puzzle piece. Also, maintain a bounding rectangle for each piece (a rectangle that minimally contains the piece in the mask image).
The way to not lose any of the original image is to arrange the masks (and the corresponding bounding rects) as a partition over the image.
Here's a link to some code that demonstrates how to apply a mask. Once the mask is applied, crop the masked image to the bounding rectangle using code like here and elsewhere.

I am also thinking that divided original image with masking but it might be bad idea for us and also complicated to manage it. So For user who is beginner for jigsaw puzzle then This is the best question/answer
and also you can get source code of jigsaw puzzle game from git.
Please hats off this man (#"Guntis Treulands") who solve your problem. I know this should be not an answer but should be comment but If I will put it as comment then may be user who have problem with jigsaw puzzle he/she can not find it easily so. I am putting it as an answer.

//Create our colorspaces
imageColorSpace = CGColorSpaceCreateDeviceRGB();
maskColorSpace = CGColorSpaceCreateDeviceGray();
provider=CGDataProviderCreateWithCFData((__bridge CFDataRef)self.puzzleData);
image=CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
//Resize the puzzle image
context = CGBitmapContextCreate(NULL, kPuzzleSize, kPuzzleSize, 8, 0, imageColorSpace, kCGImageAlphaPremultipliedFirst);
CGContextDrawImage(context, CGRectMake(0, 0, kPuzzleSize, kPuzzleSize), image);
CGImageRelease(image);
image = CGBitmapContextCreateImage(context);
CGContextRelease(context);
//Create the image view with the puzzle image
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kPuzzleSize, kPuzzleSize)];
[self.imageView setImage:[UIImage imageWithCGImage:image]];
//Create the puzzle pieces (note that pieces are rotated to the puzzle orientation in order to minimize the number of graphic operations when creating the puzzle images)
for(i = 0; i < appDelegate().puzzleSize * appDelegate().puzzleSize; ++i)
{
//Recreate the piece view
[pieces[i] removeFromSuperview];
pieces[i] = [[CJPieceView alloc] initWithFrame:CGRectMake(0, 0, kPieceSize, kPieceSize) index:i];
[pieces[i] setTag:-1];
//Load puzzle piece mask image
UIImage *maskimage=[self.arrmaskImages objectAtIndex:i];
NSData *dataMaskImage=UIImagePNGRepresentation(maskimage);
provider=CGDataProviderCreateWithCFData((__bridge CFDataRef)dataMaskImage);
tile = CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
mask = CGImageCreateCopyWithColorSpace(tile, maskColorSpace);
CGImageRelease(tile);
context = CGBitmapContextCreate(NULL, kPieceSize / kPieceShadowFactor, kPieceSize / kPieceShadowFactor, 8, 0, imageColorSpace, kCGImageAlphaPremultipliedFirst);
CGContextClipToMask(context, CGRectMake(0, 0, kPieceSize / kPieceShadowFactor, kPieceSize / kPieceShadowFactor), mask);
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0);
CGContextFillRect(context, CGRectMake(0, 0, kPieceSize / kPieceShadowFactor, kPieceSize / kPieceShadowFactor));
shadow = CGBitmapContextCreateImage(context);
CGContextRelease(context);
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kPieceSize, kPieceSize)];
[imageView setImage:[UIImage imageWithCGImage:shadow]];
[imageView setAlpha:kPieceShadowOpacity];
[imageView setUserInteractionEnabled:NO];
[pieces[i] addSubview:imageView];
CGImageRelease(shadow);
//Create image view with piece image and add it to the piece view
context = CGBitmapContextCreate(NULL, kPieceSize, kPieceSize, 8, 0, imageColorSpace, kCGImageAlphaPremultipliedFirst);
CGRect rectPiece= CGRectMake(fmodf(i, appDelegate().puzzleSize) * kPieceDistance, (floorf(i / appDelegate().puzzleSize)) * kPieceDistance, kPieceSize, kPieceSize);
[self.arrlocations addObject:[NSValue valueWithCGRect:rectPiece]];
CGContextTranslateCTM(context, (kPieceSize - kPieceDistance) / 2 - fmodf(i, appDelegate().puzzleSize) * kPieceDistance, (kPieceSize - kPieceDistance) / 2 - (appDelegate().puzzleSize - 1 - floorf(i / appDelegate().puzzleSize)) * kPieceDistance);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
subImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
tile = CGImageCreateWithMask(subImage, mask);
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kPieceSize, kPieceSize)];
[imageView setImage:[UIImage imageWithCGImage:tile]];
[imageView setUserInteractionEnabled:NO];
[pieces[i] addSubview:imageView];
CGImageRelease(tile);
CGImageRelease(subImage);
DLog(#"%f", pieces[i].frame.size.width);
pieces[i].transform=CGAffineTransformScale(CGAffineTransformIdentity, kTransformScale, kTransformScale);
DLog(#"%f %f",kTransformScale, pieces[i].frame.size.width);
//Release puzzle piece mask;
CGImageRelease(mask);
}
//Clean up
CGColorSpaceRelease(maskColorSpace);
CGColorSpaceRelease(imageColorSpace);
CGImageRelease(image);

Related

need assistance regarding growing & shrinking circle from centre in quartz-2d

I am currently working on drawing app, in which have a slider to increase and decrease line width. I just want to do a simple thing that a circle in front of slider to present a width. I easily did that but its not growing and shrinking from centre, its growing and shrinking from top x, y, here is the code
- (UIImage *)circleOnImage:(int)size
{
UIGraphicsBeginImageContext(CGSizeMake(25, 25));
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] setFill];
CGContextTranslateCTM(ctx, 12, 12);//also try to change the coordinate but didn't work
CGRect circleRect = CGRectMake(0, 0, size, size);
CGContextFillEllipseInRect(ctx, circleRect);
UIImage *retImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return retImage;
}
Try
CGContextTranslateCTM(ctx, 12.5, 12.5);
CGRect circleRect = CGRectMake(-size/2., -size/2., size, size);

UIScrollview changes UIButton's image quality on zoom out

I've got a large display area that can be panned and zoomed to view different objects. The problem that I'm running into is that the quality of the PNG images UIButton becomes somewhat degraded if I'm zoomed out (however it is back to normal when I zoom back in to 100%). It almost looks as if the image becomes oversharpened. Is this something that I'm going to have to live with, or is there a way to get rid of this grainy edge effect? The aspect ratio of the images are always 1:1, by the way.
I was able to solve this by using the answer found here in my scrollViewDidEndZooming method. Here is my code:
Resize function
- (UIImage *)resizeImage:(UIImage*)image newSize:(CGSize)newSize {
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = image.CGImage;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// Set the quality level to use when rescaling
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height);
CGContextConcatCTM(context, flipVertical);
// Draw into the context; this scales the image
CGContextDrawImage(context, newRect, imageRef);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
UIGraphicsEndImageContext();
return newImage;
}
ScrollView Method
(Widget is a UIViewController subclass which contains a button and a "widgetImage" which stores the full resolution of the image that the button should display)
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
for(Widget *theWidget in widgets){
UIImage *newScaledImage = [self resizeImage:theWidget.widgetImage newSize:CGSizeMake(theWidget.view.frame.size.width * scale, theWidget.view.frame.size.height * scale)];
[theWidget.widgetButton setImage:newScaledImage forState:UIControlStateNormal];
// theWidget.widgetButton.currentImage = newScaledImage;
}
}

How to draw a NSImage like images in NSButtons (with a deepness)?

Is there any way to draw an NSImage like images in NSButtons or other cocoa interface elements?
Here are examples:
Apple uses pdf's with black icons:
If you simply want this effect to be applied when you use your own images in a button, use [myImage setTemplate:YES]. There is no built-in way to draw images with this effect outside of a button that has the style shown in your screenshots.
You can however replicate the effect using Core Graphics. If you look closely, the effect consists of a horizontal gradient, a white drop shadow and a dark inner shadow (the latter is the most difficult).
You could implement this as a category on NSImage:
//NSImage+EtchedDrawing.h:
#interface NSImage (EtchedImageDrawing)
- (void)drawEtchedInRect:(NSRect)rect;
#end
//NSImage+EtchedDrawing.m:
#implementation NSImage (EtchedImageDrawing)
- (void)drawEtchedInRect:(NSRect)rect
{
NSSize size = rect.size;
CGFloat dropShadowOffsetY = size.width <= 64.0 ? -1.0 : -2.0;
CGFloat innerShadowBlurRadius = size.width <= 32.0 ? 1.0 : 4.0;
CGContextRef c = [[NSGraphicsContext currentContext] graphicsPort];
//save the current graphics state
CGContextSaveGState(c);
//Create mask image:
NSRect maskRect = rect;
CGImageRef maskImage = [self CGImageForProposedRect:&maskRect context:[NSGraphicsContext currentContext] hints:nil];
//Draw image and white drop shadow:
CGContextSetShadowWithColor(c, CGSizeMake(0, dropShadowOffsetY), 0, CGColorGetConstantColor(kCGColorWhite));
[self drawInRect:maskRect fromRect:NSMakeRect(0, 0, self.size.width, self.size.height) operation:NSCompositeSourceOver fraction:1.0];
//Clip drawing to mask:
CGContextClipToMask(c, NSRectToCGRect(maskRect), maskImage);
//Draw gradient:
NSGradient *gradient = [[[NSGradient alloc] initWithStartingColor:[NSColor colorWithDeviceWhite:0.5 alpha:1.0]
endingColor:[NSColor colorWithDeviceWhite:0.25 alpha:1.0]] autorelease];
[gradient drawInRect:maskRect angle:90.0];
CGContextSetShadowWithColor(c, CGSizeMake(0, -1), innerShadowBlurRadius, CGColorGetConstantColor(kCGColorBlack));
//Draw inner shadow with inverted mask:
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef maskContext = CGBitmapContextCreate(NULL, CGImageGetWidth(maskImage), CGImageGetHeight(maskImage), 8, CGImageGetWidth(maskImage) * 4, colorSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
CGContextSetBlendMode(maskContext, kCGBlendModeXOR);
CGContextDrawImage(maskContext, maskRect, maskImage);
CGContextSetRGBFillColor(maskContext, 1.0, 1.0, 1.0, 1.0);
CGContextFillRect(maskContext, maskRect);
CGImageRef invertedMaskImage = CGBitmapContextCreateImage(maskContext);
CGContextDrawImage(c, maskRect, invertedMaskImage);
CGImageRelease(invertedMaskImage);
CGContextRelease(maskContext);
//restore the graphics state
CGContextRestoreGState(c);
}
#end
Example usage in a view:
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor colorWithDeviceWhite:0.8 alpha:1.0] set];
NSRectFill(self.bounds);
NSImage *image = [NSImage imageNamed:#"MyIcon.pdf"];
[image drawEtchedInRect:self.bounds];
}
This would give you the following result (shown in different sizes):
You may need to experiment a bit with the gradient colors and offset/blur radius of the two shadows to get closer to the original effect.
If you don't mind calling a private API, you can let the operating system (CoreUI) do the shading for you. You need a few declarations:
typedef CFTypeRef CUIRendererRef;
extern void CUIDraw(CUIRendererRef renderer, CGRect frame, CGContextRef context, CFDictionaryRef object, CFDictionaryRef *result);
#interface NSWindow(CoreUIRendererPrivate)
+ (CUIRendererRef)coreUIRenderer;
#end
And for the actual drawing:
CGRect drawRect = CGRectMake(x, y, width, height);
CGImageRef cgimage = your_image;
CFDictionaryRef dict = (CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
#"backgroundTypeRaised", #"backgroundTypeKey",
[NSNumber numberWithBool:YES], #"imageIsGrayscaleKey",
cgimage, #"imageReferenceKey",
#"normal", #"state",
#"image", #"widget",
[NSNumber numberWithBool:YES], #"is.flipped",
nil];
CUIDraw ([NSWindow coreUIRenderer], drawRect, cg, dict, nil);
CGImageRelease (cgimage);
This will take the alpha channel of cgimage and apply the embossing effect as seen on toolbar buttons. You may or may not need the "is.flipped" line. Remove it if your result is upside-down.
There are a bunch of variations:
kCUIPresentationStateKey = kCUIPresentationStateInactive: The window is not active, the image will be lighter.
state = rollover: Only makes sense with the previous option. This means you are hovering over the image, the window is inactive, but the button is sensitive (click-through is enabled). It will become darker.
state = pressed: Occurs when the button is pressed. The icon gets slightly darker.
Bonus tip: To find out stuff like this, you can use the SIMBL plugin CUITrace. It prints out all the CoreUI invocations of a target app. This is a treasure trove if you have to draw your own native-looking UI.
Here's a much simpler solution: just create a cell and let it draw. No mucking around with private APIs or Core Graphics.
Code could look similar to the following:
NSButtonCell *buttonCell = [[NSButtonCell alloc] initImageCell:image];
buttonCell.bordered = YES;
buttonCell.bezelStyle = NSTexturedRoundedBezelStyle;
// additional configuration
[buttonCell drawInteriorWithFrame: someRect inView:self];
You can use different cells and configurations depending on the look you want to have (eg. NSImageCell with NSBackgroundStyleDark if you want the inverted look in a selected table view row)
And as a bonus, it will automatically look correct on all versions of OS X.
To get to draw correctly within any rect, the CGContextDrawImage and CGContextFillRect for the inner mask must have the origin of (0,0). then when you draw the image for the inner shadow you can then reuse the mask rect. So ends up looking like:
CGRect cgRect = CGRectMake( 0, 0, maskRect.size.width, maskRect.size.height );
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef maskContext = CGBitmapContextCreate( NULL, CGImageGetWidth( maskImage ), CGImageGetHeight( maskImage ), 8, CGImageGetWidth( maskImage ) * 4, colorSpace, kCGImageAlphaPremultipliedLast );
CGColorSpaceRelease( colorSpace );
CGContextSetBlendMode( maskContext , kCGBlendModeXOR );
CGContextDrawImage( maskContext, cgRect, maskImage );
CGContextSetRGBFillColor( maskContext, 1.0, 1.0, 1.0, 1.0 );
CGContextFillRect( maskContext, cgRect );
CGImageRef invertedMaskImage = CGBitmapContextCreateImage( maskContext );
CGContextDrawImage( context, maskRect, invertedMaskImage );
CGImageRelease( invertedMaskImage );
CGContextRelease( maskContext );
CGContextRestoreGState( context );
You also have to leave a 1px border around the outside of the image or the shadows won't work correctly.

Saving 2 UIImages to one while saving rotation, resize info and its quality

I want to save 2 UIImages that are moved, resized and rotated by user. The problem is i dont want to use such function as any 'printscreen one', because it makes both images to lose a lot from their quality (resolution).
Atm i use something like this:
UIGraphicsBeginImageContext(image1.size);
[image1 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];
[image2 drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];
UIImage *resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
However ofc it just adds two images, their rotation, resizing and moving isn't operated here. Can anybody help with considering these 3 aspects in coding? Any help is appreciated!
My biggest thanks in advance :)
EDIT: images can be rotated and zoomed by user (handling touch events)!
You have to set the transform of the context to match your imageView's transform before you start drawing into it.
i.e.,
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, boundingRect.size.width/2, boundingRect.size.height/2);
transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(context, transform);
// Draw the image into the context
CGContextDrawImage(context, CGRectMake(-imageView.image.size.width/2, -imageView.image.size.height/2, imageView.image.size.width, imageView.image.size.height), imageView.image.CGImage);
// Get an image from the context
rotatedImage = [UIImage imageWithCGImage: CGBitmapContextCreateImage(context)];
and check out Creating a UIImage from a rotated UIImageView.
EDIT: if you don't know the angle of rotation of the image you can get the transform from the layer property of the UIImageView:
UIGraphicsBeginImageContext(rotatedImageView.image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform transform = rotatedImageView.transform;
transform = CGAffineTransformScale(transform, 1.0, -1.0);
CGContextConcatCTM(context, transform);
// Draw the image into the context
CGContextDrawImage(context, CGRectMake(0, 0, rotatedImageView.image.size.width, rotatedImageView.image.size.height), rotatedImageView.image.CGImage);
// Get an image from the context
UIImage *newRotatedImage = [UIImage imageWithCGImage: CGBitmapContextCreateImage(context)];
UIGraphicsEndImageContext();
You will have to play about with the transform matrix to centre the image in the context and you will also have to calculate a bounding rectangle for the rotated image or it will be cropped at the corners (i.e., rotatedImageView.image.size is not big enough to encompass a rotated version of itself).
Try this:
UIImage *temp = [[UIImage alloc] initWithCGImage:image1 scale:1.0 orientation: yourOrientation];
[temp drawInRect:CGRectMake(0, 0, image1.size.width, image1.size.height)];
Similarly for image2. Rotation and resizing are handled by orientation and scale respectively. yourOrientation is a UIImageOrientation enum variable and can have a value from 0-7(check this apple documentation on different UIImageOrientation values). Hope it helps...
EDIT: To handle rotations, just write the desired orientation for the rotation you require. You can rotate 90 deg left/right or flip vertically/horizontally. For eg, in the apple documentation, UIImageOrientationUp is 0, UIImageOrientationDown is 1 and so on. Check out my github repo for an example.

Image Cropping API for iOS

Is there any cropping image API for objective C that crops images dynamically in Xcode project? Please provide some tricks or techniques how could I crop camera images in iPhone.
You can use below simple code to crop an image. You have to pass the image and the CGRect which is the cropping area. Here, I crop image so that I get center part of original image and returned image is square.
// Returns largest possible centered cropped image.
- (UIImage *)centerCropImage:(UIImage *)image
{
// Use smallest side length as crop square length
CGFloat squareLength = MIN(image.size.width, image.size.height);
// Center the crop area
CGRect clippedRect = CGRectMake((image.size.width - squareLength) / 2, (image.size.height - squareLength) / 2, squareLength, squareLength);
// Crop logic
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
UIImage * croppedImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return croppedImage;
}
EDIT - Swift Version
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
All these solutions seem quite complicated and many of them actually degrade the quality the image.
You can do much simpler using UIImageView's out of the box methods.
Objective-C
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
[self.imageView setClipsToBounds:YES];
[self.imageView setImage:img];
This will crop your image based on the dimensions you've set for your UIImageView (I've called mine imageView here).
It's that simple and works much better than the other solutions.
You can use CoreGraphics framework to cropping image dynamically.
Here is a example code part of dynamic image crop. I hope this will be helpful for you.
- (void)drawMaskLineSegmentTo:(CGPoint)ptTo withMaskWidth:(CGFloat)maskWidth inContext:(NXMaskDrawContext)context{
if (context == nil)
return;
if (context.count <= 0){
[context addObject:[NSValue valueWithCGPoint:ptTo]];
return;
}
CGPoint ptFrom = [context.lastObject CGPointValue];
UIGraphicsBeginImageContext(self.maskImage.size);
[self.maskImage drawInRect:CGRectMake(0, 0, self.maskImage.size.width, self.maskImage.size.height)];
CGContextRef graphicsContext = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(graphicsContext, kCGBlendModeCopy);
CGContextSetRGBStrokeColor(graphicsContext, 1, 1, 1, 1);
CGContextSetLineWidth(graphicsContext, maskWidth);
CGContextSetLineCap(graphicsContext, kCGLineCapRound);
CGContextMoveToPoint(graphicsContext, ptFrom.x, ptFrom.y);
CGContextAddLineToPoint(graphicsContext, ptTo.x, ptTo.y);
CGContextStrokePath(graphicsContext);
self.maskImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIGraphicsBeginImageContext(self.displayableMaskImage.size);
[self.displayableMaskImage drawInRect:CGRectMake(0, 0, self.displayableMaskImage.size.width, self.displayableMaskImage.size.height)];
graphicsContext = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(graphicsContext, kCGBlendModeCopy);
CGContextSetStrokeColorWithColor(graphicsContext, self.displayableMaskColor.CGColor);
CGContextSetLineWidth(graphicsContext, maskWidth);
CGContextSetLineCap(graphicsContext, kCGLineCapRound);
CGContextMoveToPoint(graphicsContext, ptFrom.x, ptFrom.y);
CGContextAddLineToPoint(graphicsContext, ptTo.x, ptTo.y);
CGContextStrokePath(graphicsContext);
self.displayableMaskImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[context addObject:[NSValue valueWithCGPoint:ptTo]];
}
Xcode 5, iOS 7, and 4-inch screen example: Here is an open source example of a
SimpleImageCropEditor (Project Zip and Source Code Example. You can load the Image Crop Editor as a Modal View Controller and reuse. Look at the code and please leave constructive comments concerning if this example code answers the question "Image Cropping API for iOS".
Demonstrates, is example source Objective-C code, use of UIImagePickerController, #protocol, UIActionSheet, UIScrollView, UINavigationController, MFMailComposeViewController, and UIGestureRecognizer.