iOS 환경 이미지 처리방식

  • UIImage 처리 방식은 크게 Load, Decode ,Render 세 가지 프로세스가 존재한다.
    • Load : 로드 단계에서는 압축된 이미지 파일(JPEG, PNG)을 메모리에 로드 하는 단계이다.
    • Decode : 디코드 단계에서는 JPEG, PNG 파일을 GPU가 읽을 수 있도록 디코딩 작업을 하는 프로세스이다. 이 단계에서는 해당 이미지 파일들을 Image Buffer 로 디코딩 하는 단계라고 이해하면 된다.
    • Render : 이 과정에서 Image BufferFrame Buffer로 Rendering을 하며 UIImageView에 콘텐츠를 노출 시키도록한다. 이 과정은 주기적으로 화면을 rendering 한다.

img

🚀 Image Buffer란?

img

  • Image Buffer 란 이미지의 메모리를 표현을 나타내는 Buffer 이다.
  • Image Buffer 의 각 요소(Element)는 단일 픽셀의 색상과 투명도(alpha)를 나타내기 때문에 버퍼의 크기와 Image 크기는 비례한다.
  • 즉 더 큰 이미지를 표현하려면 더 많은 Image Buffuer 가 필요하다.

🎨 Frame Buffer란?

img

  • Frame Buffer 는 버퍼에서 가장 중요한 요소중 하나이다. 위에서 설명했듯이 실제 앱의 렌더링 결과를 보관하는 Buffer 이다.
  • 앱이 View Hierarchy를 변경하면 UIKit은 앱의 UIWindow 와 모든 SubViewsFrame Buffer 로 렌더링을 한다.
  • 위 예시 사진에서 가운데 이미지가 Frame Buffer 이다.
  • Frame Buffer 는 디스플레이에 표시할 각 픽셀 색상 정보를 제공하고, 디스플레이는 Frame Buffer 가 제공하는 콘텐츠를 앱에 표시한다.
  • 앱에서 아무것도 변경하지 않았다면 이전 Frame Buffer 를 재사용 하며(동일한 데이터를 사용) 만약 앱에서 View Content를 변경 하면 UIKit 은 UIWindow를 Frame Bufferre-Render 시키고, 하드웨어는 Frame Buffer 에서 새로운 정보를 얻어와 앱에 표시한다.

📁 Data Buffer란?

img

  • Data Buffer는 Bytes의 Sequence를 포함하는 버퍼로 이미지 크기같은 Metadata와 JPEG,PNG같은 이미지 형식으로 Encode된 이미지 데이터가 저장 된다.
  • 즉 서버에서 이미지를 다운로드 하면 이미지를 Encoding해서 데이터로 받아오는데(Binary Data) 그 데이터를 담은 버퍼를 의미한다.
  • Data Buffer는 각 픽셀들이 가진 색상과 투명도 정보가 없기 때문에 Frame Buffer로 바로 변환 할 수 없다. 쉽게 말해 UIImage로 변환 하는 과정이 필요하다.

🎮 Data Buffer의 Render링 과정

img

  • 먼저 UIImage는 Data Buffer에 저장된 이미지 크기만큼 Image Buffer 를 할당 한다.
  • UIImageView는 contentmode에 맞게 Decoding 작업을 수행한다.
  • 마지막으로 UIKit이 UIImageView에 Rendering을 요청하면 Image Buffer에 저장되어 있는 이미지 데이터(색상, 투명도)를 Frame Buffer에 복사하고 크기를 조정하여 앱에 표시한다.

UIImageView Rendering 과정의 문제점

img

  • Decoding된 이미지 데이터는 Image Buffer 에 보관되기 때문에 이미지 크기에 비례한 메모리 할당이 필요하다. (요기가 중요!! DownSampling)
  • 대규모 메모리 할당이 일어나면 OS는 물리적 메모리 영억을 압축하게 된다.
  • 이러한 작업은 CPU를 사용하기 때문에 디바이스의 CPU 사용량이 증가하고, OS가 백그라운드 프로세스 부터 순차적으로 종료시키게 된다.

🎉 DownSampling

되집어 가는 Rendering 방식

img

  • Frame BufferImage Buffer 를 복사할때 모든 픽셀을 사용하는 것이 아니다. 즉 위(UIImageView Rendering 과정의 문제점) 에서 설명했듯이 Image Buffer 의 모든 픽셀을 다 사용하지 않음에도 불구하고 불필요한 메모리 낭비를 하고 있다.
  • UIImageViewSizescale 등을 고려하여 필요한 픽셀의 정보만 복사된다.
  • 만약 표시할 UIImageView의 크기가 이미지보다 작을 경우 메모리 양을 줄이기 위해 Downsampling을 고려할 수 있다.

DownSamling 방식

img

  • 원본 이미지를 UIImageView 사이즈 혹은 원하는 Size 만큼 렌더링을 통해 축소하여 썸네일을 만든다.
  • 썸네일을 만든 뒤에 Data Buffer 를 없애면 그만큼 메모리 사용량을 줄일 수 있다.
  • 그 이후 썸네일을 Decode 하면 할당하는 Image Buffer의 크기도 줄일 수 있다.
  • 이러한 과정을 통해 최소한의 메모리 사용량을 가질수 있다.

📲 DownSampling 코드 분석

전체 코드

img

1️⃣

img

  • CGImageSource 객체를 생성 한다.
  • kCGImageSourceShouldCache 는 이미지를 디코딩된 형식으로 캐싱할지 설정 하는 옵션이다.
  • WWDC 2018에서는 Data Buffer 는 DownSampling 이후 제거할 것이기에 kCGImageSourceShouldCache false로 지정하였다.

2️⃣

img

  • scale과 렌더링할 UIImageView 사이즈에 맞춰 썸네일의 최대 크기를 계산한다.
  • kCGImageSourceCreateThumbnailFromImageAlways 는 이미지 원본 파일에 썸네일이 있어도 전체 이미지를 이용해 썸네일을 만들지 결정한다. mageSourceThumbnailMaxPixelSize를 지정하지 않으면 썸네일의 크기는 전체 이미지 크기가 된다. 위 코드에서는 항상 썸네일을 만들도록 설정했다.
  • kCGImageSourceShouldCacheImmediately 는 썸네일을 생성할 때 이미지 버퍼를 생성하라고 알려준다. 이 옵션이 가장 중요한데 Core Graphics에게 지금 썸네일이 생성되었으니, 디코딩된 이미지 버퍼를 생성하라고 알려주기 때문이다. 이 옵션을 통해 디코딩에 쓰이는 CPU Hit 순간을 정확히 제어할 수 있다.
  • kCGImageSourceCreateThumbnailWithTransform : 원본 이미지의 방향 및 비율에 맞게 썸네일을 회전하고 scaling 할지 결정하는 옵션이다.
  • kCGImageSourceThumbnailMaxPixelSize : 썸네일 이미지의 최대 가로, 세로입니다. point가 아니라 픽셀 단위로 지정해야 한다. 이 옵션을 지정하지 않으면 썸네일의 크기가 원본 이미지 크기와 동일하게 설정된다.

3️⃣

img

  • 마지막으로 CGImage 타입으로 downsampling한 썸네일 이미지를 생성한다.
  • 최종적으로는 UIImage로 쓰기 위해 변환을 해주고 리턴을 해준다.
profile
"Jenny 있게 iOS 개발을 하며 대체 불가능한 인재가 되자"

0개의 댓글

관련 채용 정보

Powered by GraphCDN, the GraphQL CDN