Giulio Colleluori
Giulio Colleluori

Reputation: 1361

Scaling down UIImage and keep it sharp

I'm using the answer from this question to scale down an image and make it not blurry. However, that is not working for me. I'm using Swift. I do everything like in the answer. This is the code of the function that I use :

func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) {
        let newRect = CGRectIntegral(CGRectMake(0,0, newSize.width, newSize.height))
        let imageRef = image.CGImage

        UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
        let context = UIGraphicsGetCurrentContext()

        // Set the quality level to use when rescaling
        CGContextSetInterpolationQuality(context, CGInterpolationQuality.High)
        let flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, newSize.height)

        CGContextConcatCTM(context, flipVertical)
        // Draw into the context; this scales the image
        CGContextDrawImage(context, newRect, imageRef)

        let newImageRef = CGBitmapContextCreateImage(context)! as CGImage
        let newImage = UIImage(CGImage: newImageRef)

        // Get the resized image from the context and a UIImage
        UIGraphicsEndImageContext()

        return newImage
    }

And this is where I call it :

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {

        let identifier = "mypin"

        if annotation.isKindOfClass(MKUserLocation) {
            return nil
        }

        let detailButton: UIButton = UIButton(type: UIButtonType.DetailDisclosure)

        var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)

        if annotationView == nil
        {
            annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin")
            annotationView!.canShowCallout = true
            let pinImage = UIImage(named: "Pin.png")
            let resizedImage = resizeImage(pinImage!, newSize: CGSize(width: 12, height: 20))
            annotationView?.image = resizedImage
            annotationView!.rightCalloutAccessoryView = detailButton
        }
        else
        {
            annotationView!.annotation = annotation
        }

        return annotationView
    }

If anybody knows why this could be happening I would really appreciate it if you could let me know. Thanks in advance.

Upvotes: 0

Views: 597

Answers (2)

Hamish
Hamish

Reputation: 80781

As I suspected in my comment, it's due to the fact that CGBitmapContextCreateImage will ignore the scale that you input into UIGraphicsBeginImageContextWithOptions – and instead always create an image with a scale of 1.0.

For example if you have a 50 x 50 image context at a 3x scale:

  • CGBitmapContextCreateImage will return a 150 x 150 image at 1x scale

  • UIGraphicsGetImageFromCurrentImageContext will return a 50 x 50 image at 3x scale

Usually this shouldn't actually make a difference when displaying the image on-screen, unless you rely on the size to be specified in points rather than pixels. However, for an MKAnnotationView, the documentation says:

Assigning a new image to this property also changes the size of the view’s frame so that it matches the width and height of the new image.

Therefore if you assign it an image with a 1x scale (and your screen's scale is higher than 1x) – it will be unnecessarily scaled up, therefore losing quality. The fix therefore is to use UIGraphicsGetImageFromCurrentImageContext.

Upvotes: 1

Gene De Lisa
Gene De Lisa

Reputation: 3838

Is this any better? Yes it skips a lot, but for my annotations it works fine.

func resizeImage(image: UIImage, newSize: CGSize) -> (UIImage) {
 let scale: CGFloat = 0.0 //whatever

 UIGraphicsBeginImageContextWithOptions(newSize, true, scale)
 image.drawInRect(CGRect(origin: CGPointZero, size: newSize))

 let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
 UIGraphicsEndImageContext()

Upvotes: 0

Related Questions