How to combine/ merge 2 images into 1
Asked Answered
S

8

35

I would like to know what to do to save 2 images into 1 image.

One of the photos can be moved, rotated and zoomed in/out...

I'm doing this, but it basically captures all the stuff on the screen including my buttons...

UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *savedImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Sniper answered 13/2, 2012 at 9:17 Comment(3)
check this link:-#6690944Ferrotype
thread you linked ... not very helpfull :|Alatea
check #9209451Wisp
A
11
  1. Create a subview for adding images.
  2. Add all your images in that view instead of the main view.
  3. Let the buttons and other stuff stay on the main view.
  4. Render only the view with images in the bitmap context instead of the main view like you are doing right now.
Argyll answered 13/2, 2012 at 9:37 Comment(1)
Thanks i guess this the best way :)Sniper
A
69

You can create graphics context and draw both images in it. You'll get an image result from both your source images combined.

- (UIImage*)imageByCombiningImage:(UIImage*)firstImage withImage:(UIImage*)secondImage {
    UIImage *image = nil;

    CGSize newImageSize = CGSizeMake(MAX(firstImage.size.width, secondImage.size.width), MAX(firstImage.size.height, secondImage.size.height));
    if (UIGraphicsBeginImageContextWithOptions != NULL) {
        UIGraphicsBeginImageContextWithOptions(newImageSize, NO, [[UIScreen mainScreen] scale]);
    } else {
        UIGraphicsBeginImageContext(newImageSize); 
    }
    [firstImage drawAtPoint:CGPointMake(roundf((newImageSize.width-firstImage.size.width)/2), 
                                        roundf((newImageSize.height-firstImage.size.height)/2))]; 
    [secondImage drawAtPoint:CGPointMake(roundf((newImageSize.width-secondImage.size.width)/2), 
                                         roundf((newImageSize.height-secondImage.size.height)/2))]; 
    image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
Alatea answered 13/2, 2012 at 11:49 Comment(8)
Hi Vlad, Thanks for the code. it works, but is does not save any movement or rotation i edited. Also it save in the picture's aspect ratio. any suggestion ?Sniper
glad it works. Not sure though what you're after? at you editing the images on runtime ? or just zoom, move and rotate?Alatea
just zoom, move and rotate. then save for bumpingSniper
This function fails when the two images have the same size (then MAX(first-width, second-width) results in 0; Also, the drawAtPoint functions seem to be borked. I had to exchange them with this: [firstImage drawAtPoint:CGPointMake(0,0)]; [secondImage drawAtPoint:CGPointMake(0, firstImage.size.height)]; This only works if the two images have the same width!Outstrip
It will not fail, it will draw both images starting {0, 0}, overlapped. Coordinates are relative to the container view (ex: image inside imageView).Alatea
hello! this great code helped me very much. But I'm not sure its really necessary to use roundf() function. It may cause to round error. Maybe not error exactly but unexpected result.Dabber
Here's a gist for the same logic in swift :) gist.github.com/A-Zak/3c38d3f83f911a25790fDiscontinuation
for me the image size increases 6 to 7 times. Any way to keep the size less with same quality?Chide
C
55

Swift 4.2

let topImage = UIImage(named: "image1.png")
let bottomImage = UIImage(named: "image2.png")

let size = CGSize(width: topImage!.size.width, height: topImage!.size.height + bottomImage!.size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

topImage!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: topImage!.size.height))
bottomImage!.draw(in: CGRect(x: 0, y: topImage!.size.height, width: size.width, height: bottomImage!.size.height))

let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()

//set finalImage to IBOulet UIImageView
mergeImage.image = newImage

Objective-C

UIImage *image1 = [UIImage imageNamed:@"image1.png"];
UIImage *image2 = [UIImage imageNamed:@"image2.png"];

CGSize size = CGSizeMake(image1.size.width, image1.size.height + image2.size.height);

UIGraphicsBeginImageContext(size);

[image1 drawInRect:CGRectMake(0,0,size.width, image1.size.height)];
[image2 drawInRect:CGRectMake(0,image1.size.height,size.width, image2.size.height)];

UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

//set finalImage to IBOulet UIImageView
imageView.image = finalImage;
Cru answered 5/12, 2013 at 6:45 Comment(0)
A
11
  1. Create a subview for adding images.
  2. Add all your images in that view instead of the main view.
  3. Let the buttons and other stuff stay on the main view.
  4. Render only the view with images in the bitmap context instead of the main view like you are doing right now.
Argyll answered 13/2, 2012 at 9:37 Comment(1)
Thanks i guess this the best way :)Sniper
C
10

Swift 3

In this example the frontImage is being drawn inside the other image using a insetBy of 20% margin.

The background image must be drawn before and then the front image in sequence.

I used this to place a "Play" icon image in front a video frame image inside a UIImageView like below:

enter image description here

Usage:

self.image = self.mergedImageWith(frontImage: UIImage.init(named: "play.png"), backgroundImage: UIImage.init(named: "backgroundImage.png")))

Method:

func mergedImageWith(frontImage:UIImage?, backgroundImage: UIImage?) -> UIImage{

    if (backgroundImage == nil) {
        return frontImage!
    }

    let size = self.frame.size
    UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

    backgroundImage?.draw(in: CGRect.init(x: 0, y: 0, width: size.width, height: size.height))
    frontImage?.draw(in: CGRect.init(x: 0, y: 0, width: size.width, height: size.height).insetBy(dx: size.width * 0.2, dy: size.height * 0.2))

    let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return newImage
}
Carper answered 14/12, 2016 at 16:30 Comment(0)
T
9

You can use this method, which is very dynamic and you can specify the starting position of the second image and total size of the image.

-(UIImage *) addImageToImage:(UIImage *)img withImage2:(UIImage *)img2 andRect:(CGRect)cropRect withImageWidth:(int) width     
{

    CGSize size = CGSizeMake(width,40);
    UIGraphicsBeginImageContext(size);

    CGPoint pointImg1 = CGPointMake(0,0);
    [img drawAtPoint:pointImg1];

     CGPoint pointImg2 = cropRect.origin;
     [img2 drawAtPoint: pointImg2];

     UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     return result;
}
Trichinosis answered 12/8, 2013 at 9:39 Comment(0)
P
4

Here's a method for UIImage extension to combine multiple images:

class func combine(images: UIImage...) -> UIImage {
    var contextSize = CGSizeZero

    for image in images {
        contextSize.width = max(contextSize.width, image.size.width)
        contextSize.height = max(contextSize.height, image.size.height)
    }

    UIGraphicsBeginImageContextWithOptions(contextSize, false, UIScreen.mainScreen().scale)

    for image in images {
        let originX = (contextSize.width - image.size.width) / 2
        let originY = (contextSize.height - image.size.height) / 2

        image.drawInRect(CGRectMake(originX, originY, image.size.width, image.size.height))
    }

    let combinedImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return combinedImage
}

Example:

UIImage.combine(image1, image2)
Paravane answered 7/11, 2015 at 15:45 Comment(0)
L
2

Swift-3 (IOS10.3)

extension UIImage {
    func combineWith(image: UIImage) -> UIImage {
        let size = CGSize(width: self.size.width, height: self.size.height + image.size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

        self.draw(in: CGRect(x:0 , y: 0, width: size.width, height: self.size.height))
        image.draw(in: CGRect(x: 0, y: self.size.height, width: size.width,  height: image.size.height))

        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return newImage
    }
}

Usage

let image1 = UIImage(named: "image1.jpg")
let image2 = UIImage(named: "image2.jpg")

yourImageView.image = image1?.combineWith(image: image2)
Longrange answered 19/9, 2017 at 12:2 Comment(0)
G
0

Based on Himanshu padia's response

If you want to "dynamically" combine more images (with the same aspect ratio) into one grid in Objective-C

I have used only two for this example on even/odd slots.

Formulas for equal inline and outline border:

// psx = (x - (n+1)*bs / n)
// psy = (y - (m+1)*bs / m)

Formulas for different inline and outline border

// psx = (x - (n-1)*bs + obs / n)
// psy = (y - (m-1)*bs + obs / m)

Explanation:

  • psx, psy - piece size X and y
  • x, y - original (piece) image size
  • n, m - pieces slots in grid
  • bs - border size
  • obs - outline border size

  • Why n+1 ? Because for three pieces, you need 4 borders |*|*|*|

  • Why n-1 ? Because same as above, but excluding the first and last border !*|*|*!

Code:

UIImage *image1 = [UIImage imageNamed:@"14x9blue"];
UIImage *image2 = [UIImage imageNamed:@"14x9red"];

// grid parameters
int k =0;
int m=3,n = 3;
int i=0, j=0;

CGFloat borderSize = 20.0f;
//  equal border inline and outline
// the 1 is a multiplier for easier and more dynamic sizes
// 0*borderSize is inline border only, 1 is equal, 2 is double, etc.
CGFloat outlineBorder = 1*borderSize;

CGSize size = CGSizeMake(self.gridImageView.image.size.width, self.gridImageView.image.size.height);
CGRect gridImage = CGRectMake(0,0, size.width, size.height);


// piece size
// border inline and outline    
// psx = (x - (n-1)*bs + obs / n)
// psy = (y - (m-1)*bs + obs / m)
CGFloat pieceSizeX = (size.width - (n-1)*borderSize - 2*outlineBorder) / n;
CGFloat pieceSizeY = (size.height - (m-1)*borderSize - 2*outlineBorder) / m;




UIGraphicsBeginImageContext(size);
// semi transparent fill
[[UIColor colorWithDisplayP3Red:240 green:250 blue:0 alpha:0.5] setFill];
UIRectFill(CGRectMake(0,0, size.width, size.height));

UIImage *currentImage;
for(i=0; i<m; i++) {
    for (j=0; j<n; j++) {
        if (k++%2) {
            currentImage = image1;
        } else {
            currentImage = image2;
        }
        // 10-60 , 70-120, 130-180
        [currentImage drawInRect:CGRectMake(
                outlineBorder + (i)*borderSize + i*pieceSizeX,
                outlineBorder + (j)*borderSize + j*pieceSizeY,
                pieceSizeX,
                pieceSizeY
        )
        ];
    }
}

UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

//set finalImage to IBOulet UIImageView
self.gridImageView.image = finalImage;

Grid with equal borders

Goff answered 30/10, 2018 at 17:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.