ios - pixeles - relacion de aspecto 1:1
¿Cómo combinar dos UIImages manteniendo su posición, tamaño y relación de aspecto? (1)
Estoy intentando fusionar dos UIImage
de diferentes tamaños.
Tengo UIImage A
es del siguiente tamaño: 1287 × 1662
píxeles Y UIImage B
tiene el siguiente tamaño: 200 × 200
píxeles
UIImageView
s. UIImageView
backgroundImageView del tamaño: 375 x 667
Y UIImageView
foregroundImageView del tamaño: 100 x 100
El usuario puede mover foregroundImageView
a cualquier posición sobre backgroundImageView
.
let previewImage:UIImage? = mergeImages(img: imgBackground.image!, sizeWaterMark: CGRect.init(origin: imgForeground.frame.origin, size: CGSize.init(width: 100, height: 100)), waterMarkImage: imgForeground.image!)
func mergeImages(img:UIImage, sizeWaterMark:CGRect, waterMarkImage:UIImage) -> UIImage {
let size = self.imgBackground.frame.size
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
img.draw(in: getAspectFitFrame(sizeImgView: size, sizeImage: img.size))
let frameAspect:CGRect = getAspectFitFrame(sizeImgView: sizeWaterMark.size, sizeImage: waterMarkImage.size)
let frameOrig:CGRect = CGRect(x: sizeWaterMark.origin.x+frameAspect.origin.x, y: sizeWaterMark.origin.y+frameAspect.origin.y, width: frameAspect.size.width, height: frameAspect.size.height)
waterMarkImage.draw(in: frameOrig, blendMode: .normal, alpha: 1)
let result:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return result
}
func getAspectFitFrame(sizeImgView:CGSize, sizeImage:CGSize) -> CGRect {
let imageSize:CGSize = sizeImage
let viewSize:CGSize = sizeImgView
let hfactor : CGFloat = imageSize.width/viewSize.width
let vfactor : CGFloat = imageSize.height/viewSize.height
let factor : CGFloat = max(hfactor, vfactor)
// Divide the size by the greater of the vertical or horizontal shrinkage factor
let newWidth : CGFloat = imageSize.width / factor
let newHeight : CGFloat = imageSize.height / factor
var x:CGFloat = 0.0
var y:CGFloat = 0.0
if hfactor > vfactor {
y = (sizeImgView.height - newHeight) / 2
} else {
x = (sizeImgView.width - newWidth) / 2
}
let newRect:CGRect = CGRect(x: x, y: y, width: newWidth, height: newHeight)
return newRect
}
Esto en realidad se está fusionando y dándome lo que estoy buscando. Pero está reduciendo el tamaño de la imagen fusionada. Como puede ver esta línea en la función mergeImages
.
let size = self.imgBackground.frame.size
Quiero que el tamaño sea el tamaño original de UIImage A
. Entonces, si lo cambio a esto,
let size = self.imgBackground.image!.size
Esto cambiará la ubicación de B sobre A, después de la fusión.
Para las pruebas, puede descargar y verificar el código fuente desde aquí .
¿Qué debo hacer para mantener el tamaño original tal como está mientras tengo la posición exacta de B sobre A con una relación de aspecto adecuada?
Hice las funciones de utilidad estáticas (es incluso mejor moverlas en un archivo separado) para asegurarme de que no están usando las propiedades y los métodos de la instancia de ViewController
.
En mergeImages
:
let size = self.imgBackground.frame.size
y reemplazó el size
con img.size
. Es lo mismo que usar self.imgBackground.image!.size
como describiste en la pregunta.
Como los tamaños de imagen de origen y destino son los mismos, no es necesario ajustar el aspecto y simplemente reemplazamos:
img.draw(in: getAspectFitFrame(sizeImgView: size, sizeImage: img.size))
con
img.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: img.size))
También getFactor
cálculo del factor de aspecto para separar la función getFactor
para hacer que el código sea más granular e hice que getAspectFitFrame
devuelva no solo CGRect
sino también factor de aspecto (será útil más adelante).
Así que las funciones de utilidad se ven ahora como:
static func mergeImages(img: UIImage, sizeWaterMark: CGRect, waterMarkImage: UIImage) -> UIImage {
UIGraphicsBeginImageContextWithOptions(img.size, false, UIScreen.main.scale)
img.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: img.size))
let (frameAspect, _) = getAspectFitFrame(from: sizeWaterMark.size, to: waterMarkImage.size)
let frameOrig = CGRect(x: sizeWaterMark.origin.x + frameAspect.origin.x, y: sizeWaterMark.origin.y + frameAspect.origin.y, width: frameAspect.size.width, height: frameAspect.size.height)
waterMarkImage.draw(in: frameOrig, blendMode: .normal, alpha: 1)
let result = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return result
}
static func getAspectFitFrame(from: CGSize, to: CGSize) -> (CGRect, CGFloat) {
let (hfactor, vfactor, factor) = ViewController.getFactor(from: from, to: to)
// Divide the size by the greater of the vertical or horizontal shrinkage factor
let newWidth = to.width / factor
let newHeight = to.height / factor
var x: CGFloat = 0.0
var y: CGFloat = 0.0
if hfactor > vfactor {
y = (from.height - newHeight) / 2
} else {
x = (from.width - newWidth) / 2
}
return (CGRect(x: x, y: y, width: newWidth, height: newHeight), factor)
}
static func getFactor(from: CGSize, to: CGSize) -> (CGFloat, CGFloat, CGFloat) {
let hfactor = to.width / from.width
let vfactor = to.height / from.height
return (hfactor, vfactor, max(hfactor, vfactor))
}
También necesita otra función de utilidad para calcular el origen y el tamaño de la marca de agua a escala:
static func getScaledFrame(from: CGSize, to: CGSize, target: CGRect) -> CGRect {
let (aspectFitFrame, factor) = ViewController.getAspectFitFrame(from: from, to: to)
return CGRect(
origin: CGPoint(
x: (target.origin.x - aspectFitFrame.origin.x) * factor,
y: (target.origin.y - aspectFitFrame.origin.y) * factor),
size: CGSize(width: target.width * factor, height: target.height * factor)
)
}
Ahora está listo para renderizar la imagen fusionada:
let previewImage = ViewController.mergeImages(
img: imgBackground.image!,
sizeWaterMark: ViewController.getScaledFrame(from: imgBackground.frame.size, to: imgBackground.image!.size, target: imgForeground.frame),
waterMarkImage: imgForeground.image!
)