PNG(Portable Netword Graphics)는 lossless compression을 하는 raster-graphics* 파일 포맷이다.
(*raster-graphics: bitmap image로도 불리며, dot matrix data structure를 뜻한다.)
흔히 우리가 움짤로 잘 알고있는 GIF(Graphics Interchange Format) 포맷이 PNG의 전신이라고 할 수 있다. (GIF의 특허 문제로 PNG가 개발됨)
PNG는 JPEG과 마찬가지로 인터넷 상에 가볍게 전송되는 것을 목표로 디자인되었다.
하지만 파일의 구조나 압축 등 접근 방식에서 꽤 차이가 난다.
한번 알아보자.
PNG 파일의 시작은 고정된 8바이트의 헤더로 시작한다.
Magic number라고도 하는 것 같다.
PNG Header: 89 50 4E 47 0D 0A 1A 0A
각 바이트가 의미하는 바가 있지만, 크게 중요하지는 않은 것 같아 생략한다. (자세한 내용은 wiki 참고)
PNG 헤더 뒤에는 chunk들로 이어진다.
Chunk는 PNG 파일을 구성하는 기본 단위로 여기에는 데이터가 들어갈 수도, 메타 데이터가 들어갈 수도 있다.
모든 chunk는 아래와 같은 구조를 가진다.
|--------|------------|----------------|--------|
| Length | Chunk type | Chunk Data | CRC |
|--------|------------|----------------|--------|
| 4-byte | 4-byte | 'Length' bytes | 4-byte |
|--------|------------|----------------|--------|
critical
, ancillary
두 가지가 존재하며, 4개의 ASCII 문자로 표현된다.Critical chunk는 필수 정보를 담는 컨테이너다.
Decoder는 ancillary chunk는 해석할 수 없을 경우 스킵해도 되지만, critical chunk는 반드시 읽을 수 있어야 한다.
IHDR
: Image Header. 첫 번째 chunk로 이미지 기본 정보들을 담는다.PLTE
: Color Palette. IHDR
에서 정의된 color type에 따라 있을 수도, 없을 수도 있다.IDAT
: Image Data. 압축된 이미지 데이터가 들어간다. 이미지 데이터는 여러 IDAT
chunk로 나뉘어 저장된다.IDAT
chunk가 있어야 decode가 가능하다)IEND
: End of the Image. 이미지의 끝.Ancillary chunk는 critical chunk에서 담지 않은 다른 이미지 속성들을 담을 수 있는 컨테이너다.
예를 들어, background color, textual metadata, Exif metadata 등의 chunk가 존재한다.
대표적인 ancillary chunk type 몇 가지만 여기 적어둔다.
bGRD
: 배경 색 지정.eXIf
: Exif metadata를 저장.gaMA
: Gamma value를 저장.tEXt
: text 정보를 key-value 쌍으로 저장.tIME
: 마지막 수정 시간을 저장.tRNS
: 투명도 정보 저장.마지막으로 PNG 파일 구조의 전체적인 이해를 돕기 위해 example을 추가한다. (출처: wiki)
PNG의 압축은 2단계로 진행된다.
DEFLATE 압축 방식은 LZ777과 Huffman coding을 사용하는 lossless 압축 알고리즘이다.
JPEG의 lossy compression과 비교하여 용량은 크지만, 작은 파일에서는 크게 차이나지 않는다고 한다. (당연한 말을...)
PNG에서는 압축하기 전에 압축이 더 잘 되는 형태로 만들기 위해 filtering을 사용한다.
Image line을 스캔하며 다음에 올 픽셀값을 예측(prediction)하며 실제 값과 예측 값의 차이를 encoding하는 것이다.
여러 filter method들이 있었던 것 같은데, 현재는 filter method 0만 사용된다고 한다.
그리고 filter method 0에는 5가지 filter type이 존재하는데, 간단하게 보면 다음과 같다.
.......
..CBD..
..AX...
.......
각 칸을 픽셀이라고 하자.
픽셀 X의 값을 예측하는데, filter type 1이면 A로, 2면 B로, 3이면 A와 B의 중간값으로 예측하는 이런 식이다.
이외에도 interlacing도 있었는데, 이것을 사용하면 progressive rendering이 가능해지지만 압축률은 다소 떨어질 수 밖에 없다.
이렇게 PNG의 압축과 관련된 compression, filtering, interlacing의 사용 유무는 IHDR
chunk에 모두 명시되어 있다.
GIF와의 가장 큰 차이점은 색 표현에 있다.
GIF의 경우 8-bit의 indexed color를 지원하는 데에 그치지만, PNG는 더 넓은 범위의 색 표현을 커버한다.
24-bit (8 bits per channel), 48-bit (16 bits per channel) true color, alpha channel transparency 등.
GIF는 색이 256개를 넘어가면, 색을 256개로 줄이는 posterization이 수행되어야 한다.
GIF가 PNG보다 크기가 작다고 느끼는 것은 이 때문이다.
PNG 또한 같은 조건(indexed color)으로 저장하면 대부분 크기가 작아진다.
이외에는 GIF가 움직이는 이미지를 지원하는 정도의 차이점이 있겠다.
JPEG은 사진 이미지를 PNG보다 작은 용량으로 저장할 수 있기 때문에 디지털 카메라와 같은 곳에서 많이 사용된다.
하지만 PNG는 text, line art, graphic과 같이 sharp transition이 존재하는 이미지는 JPEG보다 더 잘 압축할 수 있다.
또한 JPEG의 큰 단점 중 하나는 JPEG 형식으로 여러번 수정/저장할 경우 generation loss가 발생한다는 점이다.
사진 이미지를 저장하는 lossless 압축 방식이 있긴 하지만, 잘 지원되지 않거나 독점적으로 사용되는 등 제한적이다.
때문에 이미지의 손실을 원하지 않을 경우, PNG의 사용은 선호될 수밖에 없는 것이다.
이외에도 Exif metadata를 JPEG과 PNG에서 모두 저장할 수 있다는 것 정도가 있겠다.
이미지 포맷을 공부하며 하나하나 알아가는 것이 제법 재밌다.
컴퓨터에서 사용되는 이미지 파일들에 대해 다른 시각으로 바라보게 되는 것 같다. (왠지 그림판으로 .jpg 파일을 열면 안될 것 같은...)
PNG의 encoder/decoder 구현도 spec을 따라하면 그렇게 어렵지 않다고 하니 한번 해보면 좋을 것이다.
내용을 정리할 때는 forward compatibility에 대한 생각이 별로 없었지만, 구현을 생각해보니 critical chunk만 해석해도 이미지를 읽을 수 있는 것은 참 좋은 디자인 같다.
최대 chunk size는 몇인지 등의 정확한 정보도 알면 좋을 것 같다.IDAT
chunk에 얼만큼의 데이터씩 쪼개져서 들어가는지,