23

I am working on Jigsaw type of game where i have two images for masking, I have implemented this code for masking

- (UIImage*) maskImage:(UIImage *)image withMaskImage:(UIImage*)maskImage {

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGImageRef maskImageRef = [maskImage CGImage];

    CGContextRef mainViewContentContext = CGBitmapContextCreate (NULL, maskImage.size.width, maskImage.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

    if (mainViewContentContext==NULL)
        return NULL;

    CGFloat ratio = 0;
    ratio = maskImage.size.width/ image.size.width;
    if(ratio * image.size.height < maskImage.size.height) {
        ratio = maskImage.size.height/ image.size.height;
    } 

    CGRect rect1 = {{0, 0}, {maskImage.size.width, maskImage.size.height}};
    CGRect rect2  = {{-((image.size.width*ratio)-maskImage.size.width)/2,-((image.size.height*ratio)-maskImage.size.height)/2},{image.size.width*ratio, image.size.height*ratio}};

    CGContextClipToMask(mainViewContentContext, rect1, maskImageRef);
    CGContextDrawImage(mainViewContentContext, rect2, image.CGImage);

    CGImageRef newImage = CGBitmapContextCreateImage(mainViewContentContext);
    CGContextRelease(mainViewContentContext);

    UIImage *theImage = [UIImage imageWithCGImage:newImage];
    CGImageRelease(newImage);
    return theImage;
}

enter image description here

+

enter image description here

=

This is final result i got after masking.

enter image description here

now i would like to crop image in piece like enter image description here and enter image description here and so on parametrically(crop an image by transparency).

if any one has implemented such code or any idea on this scenario please share.

Thanks.

I am using this line of code for as Guntis Treulands's suggestion

int i=1;
    for (int x=0; x<=212; x+=106) {
        for (int y=0; y<318; y+=106) {
            CGRect rect = CGRectMake(x, y, 106, 106);
            CGRect rect2x = CGRectMake(x*2, y*2, 212, 212);

            UIImage *orgImg = [UIImage imageNamed:@"cat@2x.png"];
            UIImage *frmImg = [UIImage imageNamed:[NSString stringWithFormat:@"%d@2x.png",i]];
            UIImage *cropImg = [self cropImage:orgImg withRect:rect2x];

            UIImageView *tmpImg = [[UIImageView alloc] initWithFrame:rect];
            [tmpImg setUserInteractionEnabled:YES];
            [tmpImg setImage:[self maskImage:cropImg withMaskImage:frmImg]];

            [self.view addSubview:tmpImg];
            i++;
        }
    }

orgImg is original cat image, frmImg frame for holding individual piece, masked in photoshop and cropImg is 106x106 cropped image of original cat@2x.png.

my function for cropping is as following

- (UIImage *) cropImage:(UIImage*)originalImage withRect:(CGRect)rect { 
    return [UIImage imageWithCGImage:CGImageCreateWithImageInRect([originalImage CGImage], rect)]; 
}
iCoder86
  • 1,874
  • 6
  • 26
  • 46
  • 3
    I think the best way to do this is to mask every piece and then create a new picture of every new piece. – Simon May 31 '12 at 21:55
  • @Simon: This is what my concern, How can i crop an image into pieces by transparency. – iCoder86 Jun 01 '12 at 06:49
  • @iCoder86:did you get the solution for cropping image? I'm also stuck with that. Can you please share your code if you have got it working? – Kavya Indi Jan 04 '13 at 11:21
  • @Harshal Sorry to say but i did not work more on this after, I will definitely update my post once i work around it. – iCoder86 Jan 04 '13 at 11:40
  • @iCoder86: It's working for me now. But new issue have come when I've applied UIPanGesture to it. The cropped pieces doesn't get pan with the given shape but in rectangular. – Kavya Indi Jan 08 '13 at 10:07

1 Answers1

17

UPDATE 2

I became really curious to find a better way to create a Jigsaw puzzle, so I spent two weekends and created a demo project of Jigsaw puzzle.

It contains:

  • provide column/row count and it will generate necessary puzzle pieces with correct width/height. The more columns/rows - the smaller the width/height and outline/inline puzzle form.
  • each time generate randomly sides
  • can randomly position / rotate pieces at the beginning of launch
  • each piece can be rotated by tap, or by two fingers (like a real piece) - but once released, it will snap to 90/180/270/360 degrees
  • each piece can be moved if touched on its “touchable shape” boundary (which is mostly the - same visible puzzle shape, but WITHOUT inline shapes)

Drawbacks:

  • no checking if piece is in its right place
  • if more than 100 pieces - it starts to lag, because, when picking up a piece, it goes through all subviews until it finds correct piece.

UPDATE

Thanks for updated question.

I managed to get this:

enter image description here

As you can see - jigsaw item is cropped correctly, and it is in square imageView (green color is UIImageView backgroundColor).

So - what I did was:

CGRect rect = CGRectMake(105, 0, 170, 170); //~ location on cat image where second Jigsaw item will be.

UIImage *originalCatImage = [UIImage imageNamed:@"cat.png"];//original cat image

UIImage *jigSawItemMask = [UIImage imageNamed:@"JigsawItemNo2.png"];//second jigsaw item mask (visible in my answer) (same width/height as cat image.)

UIImage *fullJigSawItemImage = [jigSawItemMask maskImage:originalCatImage];//masking - so that from full cat image would be visible second jigsaw item

UIImage *croppedJigSawItemImage = [self fullJigSawItemImage withRect:rect];//cropping so that we would get small image with jigsaw item centered in it.

For image masking I am using UIImage category function: (but you can probably use your masking function. But I'll post it anyways.)

- (UIImage*) maskImage:(UIImage *)image  
{     
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

     UIImage *maskImage = self;
     CGImageRef maskImageRef = [maskImage CGImage];

     // create a bitmap graphics context the size of the image
     CGContextRef mainViewContentContext = CGBitmapContextCreate (NULL, maskImage.size.width, maskImage.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);


     if (mainViewContentContext==NULL)
          return NULL;

     CGFloat ratio = 0;

     ratio = maskImage.size.width/ image.size.width;

     if(ratio * image.size.height < maskImage.size.height) {
          ratio = maskImage.size.height/ image.size.height;
     } 

     CGRect rect1  = {{0, 0}, {maskImage.size.width, maskImage.size.height}};
     CGRect rect2  = {{-((image.size.width*ratio)-maskImage.size.width)/2 , -((image.size.height*ratio)-maskImage.size.height)/2}, {image.size.width*ratio, image.size.height*ratio}};


     CGContextClipToMask(mainViewContentContext, rect1, maskImageRef);
     CGContextDrawImage(mainViewContentContext, rect2, image.CGImage);


     // Create CGImageRef of the main view bitmap content, and then
     // release that bitmap context
     CGImageRef newImage = CGBitmapContextCreateImage(mainViewContentContext);
     CGContextRelease(mainViewContentContext);

     UIImage *theImage = [UIImage imageWithCGImage:newImage];

     CGImageRelease(newImage);

     // return the image
     return theImage;
}

PREVIOUS ANSWER

Can you prepare a mask for each piece?

For example, you have that frame image. Can you cut it in photoshop in 9 separate images, where in each image it would only show corresponding piece. (all the rest - delete).

Example - second piece mask:

enter image description here

Then you use each of these newly created mask images on cat image - each piece will mask all image, but one peace. Thus you will have 9 piece images using 9 different masks.

For larger or different jigsaw frame - again, create separated image masks.

This is a basic solution, but not perfect, as you need to prepare each peace mask separately.

Hope it helps..

Sebastiano
  • 12,289
  • 6
  • 47
  • 80
Guntis Treulands
  • 4,764
  • 2
  • 50
  • 72
  • Treilands: I had already implemented that you have suggested but it has same issue of cropping see the image here http://i.stack.imgur.com/ZN9Qk.png – iCoder86 Jun 01 '12 at 09:35
  • it's Treulands. If it's hard to rewrite - copy. But anyways - indeed your example looks like you have square imageviews, and images are cropped to fit in square. Is it possible (if you haven't already tried) - imageView.contentMode = UIViewContentModeScaleAspectFit; ? – Guntis Treulands Jun 01 '12 at 09:46
  • you are clearly setting the wrong height and width, can you provide your code? – Simon Jun 01 '12 at 10:08
  • @GuntisTreulands:Thanks for your time and help i will test it and let you know... – iCoder86 Jun 01 '12 at 13:50
  • 1
    @GuntisTreulands: Hi! That can be a great idea to implement +1, but not solves my question. Any way thanks for your time.. – iCoder86 Jun 04 '12 at 08:54
  • @GuntisTreulands - you are life saver man .. really helpful and big gift for all beginner... hats off mate :) – iPatel Mar 03 '14 at 09:45
  • ? What is this ? [self fullJigSawItemImage withRect:rect]; – Zeeshan Dec 19 '14 at 05:54
  • Probably a function that does what the comment next to it mentions. Forgot to add it, sorry. But it was done in a test project, which I deleted afterwards. Your best chance is to check out provided advanced example: https://github.com/GuntisTreulands/JigsawDemo . Good luck. – Guntis Treulands Dec 19 '14 at 10:25
  • Does this support Retina display ? Don't think so . Images with exact dimensions would prevent Retina display – Ashraf Apr 16 '16 at 12:41
  • Retina display is nothing more than a different scale, as is 3x images. Simply provide correct image. Think you can detect if you are on 2x or 3x device? – Guntis Treulands Apr 16 '16 at 19:09
  • @GuntisTreulands Yea I actually did that , but I was looking for a solution to crop a 3x or 2x image to be the puzzle pieces . – Ashraf Apr 28 '16 at 07:40
  • @Ashraf When there are no solutions available, you got to create a new one! (from scratch or by adjusting existing ones :) ) – Guntis Treulands Apr 28 '16 at 10:19
  • @GuntisTreulands Wouldn't have found better than yours ;) – Ashraf Apr 28 '16 at 12:53