Skip to content

이미지에 워터마크 추가하기

Mingso KIM edited this page Dec 5, 2021 · 3 revisions

앱에서 인증샷을 찍으면 참여자의 닉네임과 촬영 시간을 사진에 워터마크처럼 넣어두는 기능을 구현하고 싶었다.

그래서 UIImage위에 UILabel을 추가하는 방식으로 구현을 하였다.

func insertText(name: String, date: String, time: String) -> UIImage {
    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(size, false, scale)

    let label = UILabel(frame: CGRect(origin: .zero, size: size))
    label.backgroundColor = UIColor(named: "SystemBackground")?.withAlphaComponent(0)
    label.textAlignment = .center
    label.numberOfLines = 2
    label.textColor = UIColor(named: "White")?.withAlphaComponent(0.5)
    label.font = UIFont.boldSystemFont(ofSize: 200)
    label.text = "\(name)\n\(date) \(time)"

    let imageView = UIImageView(image: self)
    guard let context = UIGraphicsGetCurrentContext() else { return UIImage() }
    imageView.layer.render(in: context)
    label.layer.render(in: context)
    guard let imageWithText = UIGraphicsGetImageFromCurrentImageContext() else { return UIImage() }
    UIGraphicsEndImageContext()

    return imageWithText
}

코드를 하나하나 뜯어보자면, 처음에 나오는 UIGraphicsBeginImageContextWithOptions라는 함수가 있는데,

이 함수의 기본형인 UIGraphicsBeginImageContext라는 함수를 먼저 알아보기로 하자. 애플 공식 문서에서는 아래와 같이 설명하고 있었다.

1

Creates a bitmap-based graphics context and makes it the current context. 비트맵 기반 그래픽 컨텍스트를 만들어 현재 컨텍스트를 만듭니다.

다시 말해 bitmap image context를 생성하는 함수라는 말인데, 여기서 context가 뭔지 몰라서 찾아 보았다.

코어 이미지의 모든 프로세싱은 CIContext 내에서 수행 되는데, context는 이런 렌더링 과정과 렌더링에 필요한 리소스를 더 정밀하게 control할 수 있게 해주는 객체이다. 나는 쉽게 이해하기 위해 context를 이미지를 그릴 컨버스, 스케치북.. 이런 느낌으로 이해했다.

즉, UIGraphicsBeginImageContext 는 그림을 그릴 bitmap image context를 생성하는 함수이고, 인자로는 size를 받는다.

그러면 위에서 사용한 UIGraphicsBeginImageContextWithOptions 를 찾아보자.

2

Creates a bitmap-based graphics context with the speicified options. 지정된 옵션을 사용하여 비트맵 기반 그래픽 컨텍스트를 만듭니다.

UIGraphicsBeginImageContext 에 옵션을 추가하여 그림을 그려주는 함수이다.

option으로 opaque(bool)와 scale(CGFloat)을 받는데, opaque는 말 그대로 불투명도 값이고, scale값으로 화면 크기에 맞게 이미지 크기를 조절할 수 있다.


이렇게 이미지를 그릴 컨버스(context)를 만들어 주고, UIGraphicsGetCurrentContext 로 해당 context를 가져온 후

그 위에 render(in: _) 함수로 imageView와 label을 얹어 그림을 그려준다.

그 후 UIGraphicsGetImageFromCurrentImageContext 함수로 방금 그린 context의 내용을 UIImage로 추출한다.

이러면 사진에 Text가 들어간 사진을 제대로 얻을 수 있긴 했는데, 렌더링 시간이 너무 오래걸리고, 메모리 사용량도 높게 치솟아 overflow가 되기도 했었다.

그래서 방법을 찾다가 이미지를 먼저 작게 리사이징 해준 후 렌더링을 해주는 방식으로 수정하였다.

var mainImage = originalImage.resizedImage(.original)
mainImage = mainImage.insertText(name: viewModel.userName,
                                 date: Date().toDateWithWeekdayString(),
                                 time: Date().toTimeColonString())

근데 이렇게 했더니, 이미지 크기가 매우 작아져서 label font 크기를 200에서 12로 낮춰주어 해결했다.

최종 결과물은 아래와 같다.

3

Clone this wiki locally