파일포맷 톺아보기: PNG

[원본 링크]

흔히들 이미지 받을때 보는 포맷인 PNG는 포터블 네트워크 그래픽스(Portable Network Graphics)의 축약이다.
무손실 압축 파일 포맷이라서 화질이 손실되지 않는게 특징이다.

사실 이 포맷은 처음에는 GIF의 대체품으로서 고안되었다고 한다.
당시에는 GIF의 핵심 알고리즘 중 하나에 특허가 걸려있었다나? 지금은 특허도 다 만료되어서 아무래도 상관없는 이야기다.


대충 장단점을 정리해보자면 이렇다.


장점

  1. PNG 파일은 수백 개가 아닌 수백만 개의 색상 옵션을 처리하므로 GIF보다 훨씬 더 디테일한 이미지를 저장할 수 있다.
  2. PNG 이미지는 압축해도 데이터가 손실되지 않으며, 저장과 전송이 매우 간단하다. 압축 과정에서 일부 정보가 사라지는 JPG 같은 손실 압축 포맷과 비교할 때 큰 장점이 될 수 있다.
  3. 사실상의 이미지 표준이라 JPG와 함께 가장 훌륭한 이식성을 보장한다.

단점

  1. PNG 파일은 압축 시 모든 원본 데이터를 유지하므로 GIF 또는 JPEG보다 파일 크기가 훨씬 크다.
  2. PNG는 처음부터 웹을 염두에 두고 설계되었기 때문에 CMYK 색상 모드를 지원하지 않는다. 따라서 인쇄용으로 전송하기가 어려울 수 있다.

한번 파일을 직접 까보면서 이야기해보자
이걸 다운받아서

첨부파일PNG_example.png파일 다운로드 아무 언어로나, 바이트로 읽어서 바이트열을 출력해보자.
나는 Rust로 했지만, 뭘로 해도 상관없다.

fn main() {
    if let Ok(bytes) = std::fs::read("PNG_example.png") {
        println!("Bytes: {bytes:?}");
    }
}

그러면 이런 식으로 잔뜩 찍힐 것이다.

자. 이 방대한 바이트 숫자 시퀀스가 PNG 파일의 정체다.
무질서해보지만 다 나름의 규칙과 구조가 있다. 그 개략을 살펴보도록 하겠다.




파일 헤더

PNG 파일의 처음 8바이트는 항상 고정적으로 들어가는 헤더 정보다.

데이터로서 의미를 가지는게 아니라 이게 PNG 파일임을 알려주는 정도로만 쓴다고 봐도 된다.
2번째, 3번째, 4번째 말고는 딱히 의미있게 사용되지는 않는듯하다.

일단 쓰임새를 나열하자면 이렇다.

  1. 137=0x89는 통신 등에서 8비트 데이터를 지원하지 않는 시스템을 찾거나 텍스트 파일과의 구분을 위해 사용된다.

  2. 80, 78, 71은 이게 PNG 파일임을 명시한다. 저게 뭐냐 하면, 사실 PNG를 아스키 코드로 나열한 것이다.

그래서 뭐든간에 2-4번째 바이트열이 PNG면 PNG 파일이라고 추정할 수 있다. 일반적인 유틸리티들도 보통 이런 방식으로 파일 포맷을 추측한다.

  1. 13=0x0D, 10=0x0A는 DOS-style의 줄바꿈(CRLF)으로, DOS-Unix 변환에서 데이터 줄바꿈을 위해 쓰인다.

  2. 26=0x1A는 DOS에서 TYPE 명령이 쓰였을 때 출력을 멈추기 위해 사용된다. (end-of-file).

  3. 마지막의 10=0x0A는 Unix-style 줄바꿈으로, (LF) Unix-DOS 변환에서 줄바꿈에 사용한다.




파일 청크

헤더 다음에는 실질적으로 데이터를 구성하는 청크의 리스트로 구성이 된다.
대충 이렇다.

Length는 안에 들어있는 데이터 개수.
Chunk Type은 이게 어떤 청크인지 나타낸다.
Chunk Data는 실제 데이터나 메타데이터를 저장한다. Length를 따라가는 가변 사이즈다.
CRC는 type+data로 무결성을 검증하는 체크섬용 필드다.

내 경우에는 헤더 바로 뒤에는 이런 배열이 있다.

Length가 13이란 뜻이다.

그 다음에는 이렇게 있는데

IHDR이란 청크타입이다.

이건 반드시 첫번째 청크로 나오는 일종의 메타데이터로, 이미지의 너비, 높이, 크기 등을 저장한다.

그래서 하나씩 까보면

첫번째 0,0,1,24 바이트열은 조합하면 280이 된다. 너비가 280인 것이다.
두번째 0,0,0,210은 높이가 210이란 것이고
세번째 8,6,0은 비트 수, 그러니까 파일 크기가 48000라는 것 같다.

실제로 뷰어를 열어서 비교해보면 얼추 다 들어맞는다.

그 뒤에도 color type, compression method, filter method, interlace method 등 뭐가 잔뜩 있는데, 여기서 다 다루진 않겠다.

중간에는 PLTE과 IDAT 청크로 실질적인 이미지 데이터가 저장된다.
PLTE는 색상에 대한 특수 청크인데, 없을 수도 있다. 필수가 아니다.

IDAT(73, 68, 65, 84)가 실제 이미지 압축 데이터의 청크 타입이다.

저기있다. 저기서부터 쭉 다 이미지 데이터인 것이다.

그리고 맨 아래까지 내려보면

IEND(73, 69, 78, 68)열이 있다.
이게 이미지가 끝났음을 나타내는 메타데이터 청크다.

전체적인 틀은 이정도다.
상세한 알고리즘에 대해서는 나중에 생각이 나면 따로 정리해보겠다.


참조
https://www.adobe.com/kr/creativecloud/file-types/image/raster/png-file.html
https://ko.wikipedia.org/wiki/PNG
https://velog.io/@nurungg/PNG-image-format