Search code examples
iosswiftcgcontextquartz-2d

Encountered weird sawtooth when drawing rounded corner images on iOS


I'm trying to create thumbnail images with rounded corners and with a bar at the bottom of the image on iOS.

And I have the following code:

extension UIImage {
  func thumbnails(with options: SomeDrawingOptions) -> UIImage? {
    // ...

    UIGraphicsBeginImageContextWithOptions(targetRect.size, false, 0)
    defer {
      UIGraphicsEndImageContext()
    }
    let context = UIGraphicsGetCurrentContext()!

    UIColor.white.setFill()
    context.fill(targetRect)

    UIBezierPath(roundedRect: targetRect, cornerRadius: 8).addClip()

    // Drawing image
    draw(in: targetRect)

    // Drawing a transparent mask
    UIColor.black.withAlphaComponent(0.4).setFill()
    context.fill(targetRect)

    // Drawing bar
    UIColor.white.setFill()
    context.fill(someBarRect)

    return UIGraphicsGetImageFromCurrentImageContext()
  }
}

Finally I've got an image with sharp rounded corners just like those in the following snapshot.

Sharp Rounded Corners

Any advise to eliminate the sawtooth of rounded corners?

======= Solution added ======

Thanks the answer for @wj2061, the problem solved. And here is the code:

extension UIImage {
  func thumbnails(with options: SomeDrawingOptions) -> UIImage? {
    // ...

    let targetRect = options.targetRect
    let clippedPath = UIBezierPath(roundedRect: targetRect, cornerRadius: 8)

    func makeImage() -> UIImage? {
      UIGraphicsBeginImageContextWithOptions(targetRect.size, false, 0)
      defer { UIGraphicsEndImageContext() }
      let context = UIGraphicsGetCurrentContext()!

      draw(in: targetRect)

      // Drawing a transparent mask
      UIColor.black.withAlphaComponent(0.4).setFill()
      context.fill(targetRect)

      // Drawing bar
      UIColor.white.setFill()
      context.fill(someBarRect)

      return UIGraphicsGetImageFromCurrentContext()
    }

    UIGraphicsBeginImageContextWithOptions(targetRect.size, false, 0)
    defer { UIGraphicsEndImageContext() }
    let context = UIGraphicsGetCurrentContext()!

    // Drawing image
    clippedPath.addClip()
    makeImage()?.draw(in: targetRect)

    return UIGraphicsGetImageFromCurrentImageContext()
  }
}

Solution

  • So it seems like this pitfall has something to do with UIBezierPath(roundedRect: targetRect, cornerRadius: 8).addClip(), and I try to add mask to image in step 1, add corner to image in step 2. The code is like this

    extension UIImage {
    func thumbnails() -> UIImage? {
        let targetRect = CGRect(x: 0, y: 0, width: 100, height: 150)
    
        UIGraphicsBeginImageContextWithOptions(targetRect.size, false, 0)
        defer {
            UIGraphicsEndImageContext()
        }
    
        UIBezierPath(roundedRect: targetRect, cornerRadius: 8).addClip()
    
        let makedImage = self.makedImage()
    
        makedImage?.draw(in : targetRect)
    
        return UIGraphicsGetImageFromCurrentImageContext()
    }
    
    
    func makedImage()->UIImage?{
        let targetRect = CGRect(x: 0, y: 0, width: 100, height: 150)
        let someBarRect = CGRect(x: 0, y: 100, width: 100, height: 50)
    
        UIGraphicsBeginImageContextWithOptions(targetRect.size, false, 0)
        defer {
            UIGraphicsEndImageContext()
        }
        let context = UIGraphicsGetCurrentContext()!
    
        draw(in : targetRect)
    
    
        // Drawing a transparent mask
        UIColor.black.withAlphaComponent(0.4).setFill()
        context.fill(targetRect)
    
        // Drawing bar
        UIColor.white.withAlphaComponent(1).setFill()
        context.fill(someBarRect)
    
        return UIGraphicsGetImageFromCurrentImageContext()
     }
    }