Clamping CIImage returns strange sizes - ios

I'm trying to crop an image and extend the colors from the edges with clamping, but is returning a strange image size after that:
let image = UIImage(named: "frame_1")
var ciimage = CIImage(image: image!)
print("\(ciimage!.extent.width) x \(ciimage!.extent.height)")
// at this point it returns "480.0 x 360.0" that is ok
ciimage = ciimage!.clamping(to: CGRect(x: 50, y: 50, width: 480.0, height: 360.0))
print("\(ciimage!.extent.width) x \(ciimage!.extent.height)")
//now it returns two strange values: "1.79769313486232e+308 x 1.79769313486232e+308"
Shouldn't it return 480.0 and 360.0 after clamping? What I am doing wrong?

OK I solved the problem, cropping it after clamping:
ciimage = ciimage!.clamping(to: CGRect(x: 50, y: 50, width: 480, height: 360))
ciimage = ciimage!.cropping(to: CGRect(x: 0, y: 0, width: 480, height: 360))
The result is what I expected.

Related

Why draw(in: CGRect) draws border around ellipse when it should not?

This is my simple function I use for drawing an image in context:
let renderer=UIGraphicsImageRenderer(size: CGSize(width: 330, height: 330))
let img=renderer.image{ ctx in
let circle=CGRect(x:0,y:0,width: 330, height: 330)
ctx.cgContext.setFillColor(UIColor.white.cgColor)
ctx.cgContext.addEllipse(in: circle)
ctx.cgContext.drawPath(using: .fill)
let image = UIImage(named: "1")!
image.draw(in: CGRect(x: 80, y: 80, width: 100, height: 100))
}
And the result is following:
As you can see there is output of UIGraphicsImageRenderer with border around ellipse. Why? Border is not defined anywhere, but it is printed.
The image named 1 is the following one:
NOTE:
This issue appears only when compiling ios app. Using playground everything is fine and ok.
Does your UIImageView have a cornerRadius applied to its layer? That can cause a thin gray border like you see here. If you create a circular image, like you have with UIGraphicsImageRenderer, you should not need to do any masking or cornerRadius on the UIImageView.
If you only want to fill the path, and not stroke it, one could use fillPath rather than drawPath.
FWIW, you could also just bypass the CoreGraphics context and just fill the oval directly:
let image = renderer.image { _ in
UIColor.white.setFill()
UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 330, height: 330))
.fill()
UIImage(named: "1")!
.draw(in: CGRect(x: 80, y: 80, width: 100, height: 100))
}
OK, the updated code still does not match.
First, in your posted image, the background is not white.
Second, even accounting for that, there is no "edge" on the rendered UIImage.
So, I'm going to make a guess here....
Assuming you execute the img = renderer.image { .... code block, and then you set imageView.image = img, my suspicion is that you have something like this:
imageView.backgroundColor = .lightGray
imageView.layer.cornerRadius = imageView.frame.height / 2.0
So, the lightGray "circle" is the lightGray background anti-aliased to the .cornerRadius.
I would be that if set:
imageView.backgroundColor = .clear
and do not set the layer's cornerRadius (no need to), your ellipse border will be gone.
If it's still there, then you need to provide some code that actually reproduces the issue.
Edit
I'm still not seeing the "border" when setting the rendered image to an image view, but...
Doing some debug inspecting and using the "1" image you added, there IS a difference.
Try this, and see if it gets rid of the border:
let fmt = UIGraphicsImageRendererFormat()
fmt.preferredRange = .standard
let renderer = UIGraphicsImageRenderer(size: CGSize(width: 330, height: 330), format: fmt)
You can then use either:
let img = renderer.image { ctx in
let circle = CGRect(x:0, y:0, width: 330, height: 330)
ctx.cgContext.setFillColor(UIColor.white.cgColor)
ctx.cgContext.addEllipse(in: circle)
ctx.cgContext.drawPath(using: .fill)
if let image = UIImage(named: "1") {
image.draw(in: CGRect(x: 80, y: 80, width: 100, height: 100))
}
}
or Rob's suggested:
let img = renderer.image { _ in
UIColor.white.setFill()
UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 330, height: 330))
.fill()
if let image = UIImage(named: "1") {
image.draw(in: CGRect(x: 80, y: 80, width: 100, height: 100))
}
}

in IOS , implement 9-patch image with strange white vertical line?

Here is the case:
raw image:
gift.9
image after processing
there are two strange white vertical lines
code part:
view.backgroundColor = UIColor.white
let imgViewWidth: CGFloat = 300
let imgView = UIImageView(frame: CGRect(origin: CGPoint(x: 50, y: 250), size: CGSize(width: imgViewWidth, height: 100)))
view.addSubview(imgView)
if let image = UIImage(named: "gift.9"), let img = stretch(image: image, newWidth: imgViewWidth){
imgView.image = img
}
the logic is easy , image crop and stretch.
image crop
func crop(img: UIImage, with rect: CGRect) -> UIImage? {
guard let cgImg = img.cgImage else { return nil }
// Create bitmap image from context using the rect
if let imageRef: CGImage = cgImg.cropping(to: rect){
// Create a new image based on the imageRef and rotate back to the original orientation
let image: UIImage = UIImage(cgImage: imageRef, scale: img.scale, orientation: img.imageOrientation)
return image
}
else{
return nil
}
}
image stretch
func stretch(image: UIImage, newWidth: CGFloat) -> UIImage? {
guard image.size.width < newWidth else {
return image
}
let originalWidth = image.size.width
let offset: CGFloat = 1
// crop left first
let left = crop(img: image, with: CGRect(x: 0, y: 0, width: originalWidth/2, height: image.size.height))
// crop right first
let right = crop(img: image, with: CGRect(x: originalWidth/2, y: 0, width: originalWidth/2, height: image.size.height))
UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: image.size.height),
false, image.scale)
let lhsResizable = left?.resizableImage(withCapInsets: UIEdgeInsets(top: 0,
left: originalWidth/2 - offset,
bottom: 0,
right: 0),
resizingMode: .stretch)
// draw left stretched first
lhsResizable?.draw(in: CGRect(x: 0, y: 0, width: newWidth/2, height: image.size.height))
let rhsResizable = right?.resizableImage(withCapInsets: UIEdgeInsets(top: 0,
left: 0,
bottom: 0,
right: originalWidth/2 - offset),
resizingMode: .stretch)
// then , draw right stretched
rhsResizable?.draw(in: CGRect(x: newWidth/2, y: 0, width: newWidth/2, height: image.size.height))
let fullImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return fullImage
}
PS:
I marked the two strange vertical white line
How to get rid of it?
Any other method is welcomed.

How to erase the thin side line right bottom?

I want compound view's shape from Oval shape subtract Rect shape,
so first I create a mask image:
let paths = CGMutablePath()
//Oval shape path at left
let leftPath = CGPath(ellipseIn: CGRect(x: 0, y: 0, width: 200, height: 150), transform: nil)
//Rect shape path at right
let rightPath = CGPath(roundedRect: CGRect(x: 100, y: 100, width: 120, height: 100), cornerWidth: 8, cornerHeight: 8, transform: nil)
paths.addPath(leftPath)
paths.addPath(rightPath)
UIGraphicsBeginImageContext(CGSize(width: 220, height: 200))
let ctx = UIGraphicsGetCurrentContext()
ctx?.addPath(paths)
ctx?.clip(using: .evenOdd)
ctx?.addPath(leftPath.cgPath)
ctx?.clip(using: .evenOdd)
ctx?.setFillColor(UIColor.red.cgColor)
ctx?.fill(CGRect(x: 0, y: 0, width: 220, height: 200))
//the mask image
let maskImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
next I use this image for mask:
let maskView = UIImageView(image: maskImage)
maskView.contentMode = .center
imageView.mask = maskView
run App, I get this:
looks good?not really...
if you look carefully,you will notice a thin line at view right bottom
It's not OK for me!
How do I erase the sideline??? Thanks :)
Draw the ellipse, then clear the rounded rect.
import UIKit
let ellipse = CGPath(ellipseIn: CGRect(x: 0, y: 0, width: 200, height: 150), transform: nil)
let roundedRect = CGPath(roundedRect: CGRect(x: 100, y: 100, width: 120, height: 100), cornerWidth: 8, cornerHeight: 8, transform: nil)
let maskImage = UIGraphicsImageRenderer(size: CGSize(width: 220, height: 200)).image { rendererContext in
let ctx = rendererContext.cgContext
ctx.setFillColor(UIColor.red.cgColor)
ctx.addPath(ellipse)
ctx.fillPath()
ctx.setBlendMode(.clear)
ctx.addPath(roundedRect)
ctx.fillPath()
}
Result:

how to place a scrolling image behind a stationary image in Swift

I am trying to port an artificial horizon app I wrote for a PC in c# to swift. It has a bezel image which does not move and behind it is a horizon image which can move up and down behind the bezel. The "window" part of the bezel is yellow so in c# I just made the yellow opaque.
In swift I stated with the horizon inside of a UIScrollView but I'm not sure how to get that to work with a second image that should not scroll.
Not all that up to speed on this swift stuff, can someone point me in the right direction?
let view: UIView = UIView.init(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
let scrollView = UIScrollView.init(frame: view.bounds)
view.addSubview(scrollView)
let backImage: UIImage = fromColor(UIColor.redColor(), size: CGSize(width: 1000, height: 1000))
let backImageView: UIImageView = UIImageView.init(image: backImage)
scrollView.addSubview(backImageView)
scrollView.contentSize = CGSize.init(width: backImage.size.width, height: backImage.size.height)
let frontImage: UIImage = fromColor(UIColor.blueColor(), size: CGSize(width: 100, height: 100))
let layer: CALayer = CALayer.init()
layer.frame = CGRect.init(x: view.center.x - 50, y: view.center.y - 50, width: 100, height: 100)
layer.contents = frontImage.CGImage
view.layer.addSublayer(layer)
func fromColor(color: UIColor, size: CGSize) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, rect)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
fromColor is a helper method.
Result of the code

Merging two UIImage's

I want to merge two UIImage's but I'm having some difficulties. I'm playing around with a drawing app where the user when she or he is done will merge their final drawing with their original image they wanted to draw on.
topImage and bottomImage are both final images and need to be merged with their corresponding aspectFit aka ratio.
Then the user can at the end combine both images into one but the thing is that bottomImageView's UIImage will vary in size.
I've been trying to merge them without effecting the newImage or the Aspect ratio (fit)
Set up:
I have two UIImageView which are set to (width: 310, height: 400)
bottomImageView
bottomImage is JPEG: size ( 600, 800) //other images will vary in ratio
topImageView
topImage is PNG (for transparency): size (310, 400)
The bottomImage which is the part of that facebook post is the what the user will theoretically "draw". The topImage (red lines/circle) is the what should be merged with the bottom image.
How can I merge them together without changing the size of the UIImage's?
I've tried two different ways but to no avail.
way 1:
let size = CGSize(width: 300, height: 400)
UIGraphicsBeginImageContext(size)
let areaSize = CGRect(x: 0, y: 0, width: size.width, height: size.height)
bottomImage.drawInRect(areaSize)
topImage.drawInRect(areaSize, blendMode: CGBlendMode.Normal, alpha: 1.0)
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Way 1, doesn't work because, it creates a new canvas (300, 400) which ultimately alters the size which distorts both my bottomImage and topImage.
Way 2:
let bottomImageView: UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 310, height: 400))
let topImageView: UIImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 310, height: 400))
bottomImageView.image = topImage
topImageView.image = bottomImage
bottomImageView.contentMode = .ScaleAspectFit
topImageView.contentMode = .ScaleAspectFit
UIGraphicsBeginImageContext(bottomImageView.frame.size)
bottomImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: bottomImageView.frame.size.width, height: bottomImageView.frame.size.height), blendMode: CGBlendMode.Normal, alpha: 1.0)
topImageView.image?.drawInRect(CGRect(x:0, y: 0, width: topImageView.frame.size.width, height: topImageView.frame.size.height), blendMode: CGBlendMode.Normal, alpha: 1.0)
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsGetImageFromCurrentImageContext()
Way 2: I thought that by putting two UIImageViews then "pressing" them together would work but it ended up distorting my newImage like way 1.
Is it possible to combine them without effecting making the newImage look distorted?

Resources