UIImage 처리 방식은 크게 Load, Decode ,Render 세 가지 프로세스가 존재한다.
Load : 로드 단계에서는 압축된 이미지 파일(JPEG, PNG)을 메모리에 로드 하는 단계이다.
Decode : 디코드 단계에서는 JPEG, PNG 파일을 GPU가 읽을 수 있도록 디코딩 작업을 하는 프로세스이다. 이 단계에서는 해당 이미지 파일들을 Image Buffer 로 디코딩 하는 단계라고 이해하면 된다.
Render : 이 과정에서 Image Buffer 를 Frame Buffer로 Rendering을 하며 UIImageView에 콘텐츠를 노출 시키도록한다. 이 과정은 주기적으로 화면을 rendering 한다.
🚀 Image Buffer란?
Image Buffer 란 이미지의 메모리를 표현을 나타내는 Buffer 이다.
Image Buffer 의 각 요소(Element)는 단일 픽셀의 색상과 투명도(alpha)를 나타내기 때문에 버퍼의 크기와 Image 크기는 비례한다.
즉 더 큰 이미지를 표현하려면 더 많은 Image Buffuer 가 필요하다.
🎨 Frame Buffer란?
Frame Buffer 는 버퍼에서 가장 중요한 요소중 하나이다. 위에서 설명했듯이 실제 앱의 렌더링 결과를 보관하는 Buffer 이다.
앱이 View Hierarchy를 변경하면 UIKit은 앱의 UIWindow 와 모든 SubViews 를 Frame Buffer 로 렌더링을 한다.
위 예시 사진에서 가운데 이미지가 Frame Buffer 이다.
Frame Buffer 는 디스플레이에 표시할 각 픽셀 색상 정보를 제공하고, 디스플레이는 Frame Buffer 가 제공하는 콘텐츠를 앱에 표시한다.
앱에서 아무것도 변경하지 않았다면 이전 Frame Buffer 를 재사용 하며(동일한 데이터를 사용) 만약 앱에서 View Content를 변경 하면 UIKit 은 UIWindow를 Frame Buffer 를 re-Render 시키고, 하드웨어는 Frame Buffer 에서 새로운 정보를 얻어와 앱에 표시한다.
📁 Data Buffer란?
Data Buffer는 Bytes의 Sequence를 포함하는 버퍼로 이미지 크기같은 Metadata와 JPEG,PNG같은 이미지 형식으로 Encode된 이미지 데이터가 저장 된다.
즉 서버에서 이미지를 다운로드 하면 이미지를 Encoding해서 데이터로 받아오는데(Binary Data) 그 데이터를 담은 버퍼를 의미한다.
Data Buffer는 각 픽셀들이 가진 색상과 투명도 정보가 없기 때문에 Frame Buffer로 바로 변환 할 수 없다. 쉽게 말해 UIImage로 변환 하는 과정이 필요하다.
🎮 Data Buffer의 Render링 과정
먼저 UIImage는 Data Buffer에 저장된 이미지 크기만큼 Image Buffer 를 할당 한다.
UIImageView는 contentmode에 맞게 Decoding 작업을 수행한다.
마지막으로 UIKit이 UIImageView에 Rendering을 요청하면 Image Buffer에 저장되어 있는 이미지 데이터(색상, 투명도)를 Frame Buffer에 복사하고 크기를 조정하여 앱에 표시한다.
UIImageView Rendering 과정의 문제점
Decoding된 이미지 데이터는 Image Buffer 에 보관되기 때문에 이미지 크기에 비례한 메모리 할당이 필요하다. (요기가 중요!! DownSampling)
대규모 메모리 할당이 일어나면 OS는 물리적 메모리 영억을 압축하게 된다.
이러한 작업은 CPU를 사용하기 때문에 디바이스의 CPU 사용량이 증가하고, OS가 백그라운드 프로세스 부터 순차적으로 종료시키게 된다.
🎉 DownSampling
되집어 가는 Rendering 방식
Frame Buffer는 Image Buffer 를 복사할때 모든 픽셀을 사용하는 것이 아니다. 즉 위(UIImageView Rendering 과정의 문제점) 에서 설명했듯이 Image Buffer 의 모든 픽셀을 다 사용하지 않음에도 불구하고 불필요한 메모리 낭비를 하고 있다.
UIImageView 의 Size나 scale 등을 고려하여 필요한 픽셀의 정보만 복사된다.
만약 표시할 UIImageView의 크기가 이미지보다 작을 경우 메모리 양을 줄이기 위해 Downsampling을 고려할 수 있다.
DownSamling 방식
원본 이미지를 UIImageView 사이즈 혹은 원하는 Size 만큼 렌더링을 통해 축소하여 썸네일을 만든다.
썸네일을 만든 뒤에 Data Buffer 를 없애면 그만큼 메모리 사용량을 줄일 수 있다.
그 이후 썸네일을 Decode 하면 할당하는 Image Buffer의 크기도 줄일 수 있다.
이러한 과정을 통해 최소한의 메모리 사용량을 가질수 있다.
📲 DownSampling 코드 분석
전체 코드
1️⃣
CGImageSource 객체를 생성 한다.
kCGImageSourceShouldCache 는 이미지를 디코딩된 형식으로 캐싱할지 설정 하는 옵션이다.
WWDC 2018에서는 Data Buffer 는 DownSampling 이후 제거할 것이기에 kCGImageSourceShouldCachefalse로 지정하였다.
2️⃣
scale과 렌더링할 UIImageView 사이즈에 맞춰 썸네일의 최대 크기를 계산한다.
kCGImageSourceCreateThumbnailFromImageAlways 는 이미지 원본 파일에 썸네일이 있어도 전체 이미지를 이용해 썸네일을 만들지 결정한다. mageSourceThumbnailMaxPixelSize를 지정하지 않으면 썸네일의 크기는 전체 이미지 크기가 된다. 위 코드에서는 항상 썸네일을 만들도록 설정했다.
kCGImageSourceShouldCacheImmediately 는 썸네일을 생성할 때 이미지 버퍼를 생성하라고 알려준다. 이 옵션이 가장 중요한데 Core Graphics에게 지금 썸네일이 생성되었으니, 디코딩된 이미지 버퍼를 생성하라고 알려주기 때문이다. 이 옵션을 통해 디코딩에 쓰이는 CPU Hit 순간을 정확히 제어할 수 있다.
kCGImageSourceCreateThumbnailWithTransform : 원본 이미지의 방향 및 비율에 맞게 썸네일을 회전하고 scaling 할지 결정하는 옵션이다.
kCGImageSourceThumbnailMaxPixelSize : 썸네일 이미지의 최대 가로, 세로입니다. point가 아니라 픽셀 단위로 지정해야 한다. 이 옵션을 지정하지 않으면 썸네일의 크기가 원본 이미지 크기와 동일하게 설정된다.
iOS 환경 이미지 처리방식
JPEG
,PNG
)을 메모리에 로드 하는 단계이다.JPEG
,PNG
파일을 GPU가 읽을 수 있도록 디코딩 작업을 하는 프로세스이다. 이 단계에서는 해당 이미지 파일들을 Image Buffer 로 디코딩 하는 단계라고 이해하면 된다.🚀 Image Buffer란?
🎨 Frame Buffer란?
📁 Data Buffer란?
JPEG
,PNG
같은 이미지 형식으로 Encode된 이미지 데이터가 저장 된다.🎮 Data Buffer의 Render링 과정
UIImageView Rendering 과정의 문제점
🎉 DownSampling
되집어 가는 Rendering 방식
DownSamling 방식
📲 DownSampling 코드 분석
전체 코드
1️⃣
CGImageSource
객체를 생성 한다.kCGImageSourceShouldCache
는 이미지를 디코딩된 형식으로 캐싱할지 설정 하는 옵션이다.kCGImageSourceShouldCache
false
로 지정하였다.2️⃣
scale
과 렌더링할 UIImageView 사이즈에 맞춰 썸네일의 최대 크기를 계산한다.kCGImageSourceCreateThumbnailFromImageAlways
는 이미지 원본 파일에 썸네일이 있어도 전체 이미지를 이용해 썸네일을 만들지 결정한다.mageSourceThumbnailMaxPixelSize
를 지정하지 않으면 썸네일의 크기는 전체 이미지 크기가 된다. 위 코드에서는 항상 썸네일을 만들도록 설정했다.kCGImageSourceShouldCacheImmediately
는 썸네일을 생성할 때 이미지 버퍼를 생성하라고 알려준다. 이 옵션이 가장 중요한데 Core Graphics에게 지금 썸네일이 생성되었으니, 디코딩된 이미지 버퍼를 생성하라고 알려주기 때문이다. 이 옵션을 통해 디코딩에 쓰이는 CPU Hit 순간을 정확히 제어할 수 있다.kCGImageSourceCreateThumbnailWithTransform
: 원본 이미지의 방향 및 비율에 맞게 썸네일을 회전하고 scaling 할지 결정하는 옵션이다.kCGImageSourceThumbnailMaxPixelSize
: 썸네일 이미지의 최대 가로, 세로입니다. point가 아니라 픽셀 단위로 지정해야 한다. 이 옵션을 지정하지 않으면 썸네일의 크기가 원본 이미지 크기와 동일하게 설정된다.3️⃣
CGImage
타입으로 downsampling한 썸네일 이미지를 생성한다.📝 참고자료