주요 내용으로 건너뛰기

색상 형식

색상은 디스플레이의 픽셀을 통해 보이는 것을 말합니다. 이러한 색상은 프레임버퍼에 저장된 값에서 비롯됩니다. 일반적으로 그래픽 시스템은 표현, 사용 및 표시할 수 있는 색상의 크기가 제한되어 있습니다. 이는 TouchGFX와 TouchGFX 애플리케이션에서도 마찬가지입니다.

애플리케이션에서 가능한 픽셀 색상 수에 따라 애플리케이션의 많은 부분이 영향을 받는데, 이를테면 디스플레이에 나타나는 시각적인 모습부터 프레임버퍼의 메모리 사용량, 그리고 전반적인 성능이 달라지게 됩니다. 이 섹션에서는 TouchGFX에 사용되는 색상에 대해 자세히 살펴보고 TouchGFX에서 사용할 수 있는 색상 형식을 비롯한 장단점을 집중적으로 알아보겠습니다.

색상

TouchGFX에서는 RGB 색상이라고 알려진 빨간색, 녹색, 파란색 성분이 사용됩니다. 각 색상 성분은 0부터 255까지 다양합니다. 0은 해당 성분이 전혀 없는 것을, 그리고 255는 해당 성분이 가득한 것을 말합니다.

RGB에서 완전한 검은색은 (0,0,0), 완전한 흰색은 (255,255,255)으로 표시됩니다. 밝은 녹색은 (0,255,0), 중간 밝기의 빨간색은 (128,0,0), 어두운 보라색은 (64,0,64)입니다.

일부 RGB 색상

그레이스케일

그레이스케일(grayscale) 애플리케이션에서는 검은색에서 흰색까지 모든 색상이 회색입니다. 따라서 RGB 값이 아니라 회색 강도로 표현됩니다. 따라서 그레이스케일 색상은 R과 G, B가 동일한 RGB 색상이라고 볼 수도 있습니다.

불투명도

일부 환경에서는 색상에 불투명도를 지정하는 성분을 추가하기도 합니다. 불투명도 역시 나머지 색상 성분과 마찬가지로 0부터 255까지 다양합니다. 불투명도가 지정된 색상을 RGBA 색상이라고 합니다. 여기서 A는 알파값으로, 불투명도에 사용되는 명칭입니다.

여기서 완전히 불투명한 검은색은 (0,0,0,255)이고, 다수 투명한 빨간색은 (255,0,0,128)입니다.

흰색과 회색 위에 겹친 일부 RGBA 색상

색상이 완전히 불투명하지 않을 때는 기존 색상과 함께 혼합해야 합니다. 이러한 색상 혼합을 알파 블렌딩이라고 합니다.

색 심도

색 심도(color depth)란 프레임버퍼에 저장된 색상들을 하나씩 나타낼 때 사용되는 비트 수를 말합니다. 이러한 수치는 픽셀당 비트로 나타내며, 줄여서 bpp라고 표기합니다.

사용되는 비트 수가 많을수록 더 많은 색상을 표현할 수 있습니다.

많이 사용되는 색 심도는 24bpp입니다. 각 비트를 표시할 수도 있고, 표시하지 않을 수도 있기 때문에 표현 가능한 색상 수는 224, 즉 16777216개입니다.

또한 비교적 적게 사용되는 색 심도는 1bpp입니다. 이 색 심도는 흑백 애플리케이션에 적용됩니다. 이 말은 표현 가능한 색상이 21, 즉 2개에 불과하다는 것을 의미합니다.

TouchGFX에서 기본적으로 지원되는 색 심도는 다음과 같습니다.

  • 32bpp - 색상 16777216개와 해당하는 불투명도 값
  • 24bpp - 색상 16777216개
  • 16bpp - 색상 65536개
  • 6/8 bpp - 색상 64개
  • 4bpp - 그레이스케일 색상 16개
  • 2bpp - 그레이스케일 색상 4개
  • 1bpp - 그레이스케일 색상 2개

색상 성분 범위에 대해 알아야 할 것이 있습니다. 색 심도가 24bpp 미만일 경우 빨간색, 녹색 및 파란색 성분의 각 범위는 0~255가 아닙니다. 예를 들어 빨간색의 심도가 16bpp라고 가정할 경우 빨간색 성분의 범위는 0~31입니다. 24bpp에서 255인 것처럼 16bpp에서 가장 진한 빨간 색상을 표현하려면 값은 31이 됩니다. 이렇게 생각할 수 있는 이유는 색 심도가 16bpp인 색상들은 24bpp에서 가능한 색상의 하위 집합만 표현할 수 있기 때문입니다.

6/8bpp 심도에서 각 픽셀은 색상 정보에 6비트를 사용합니다(빨간색, 녹색 및 파란색 각각에 대해 2비트). 프레임 버퍼 액세스를 단순화하기 위해 각 픽셀이 6비트에서 8비트(1바이트)로 증가합니다. 프레임 버퍼의 추가 2비트는 사용되지 않습니다.

형식

지금까지 색상을 표현하는 데 필요한 비트의 크기에 대해 살펴보았으니, 이제 비트의 내용에 대해 더 자세히 알아보겠습니다. 하나의 색상에는 빨간색 성분을 설명하는 비트와 녹색 성분을 설명하는 비트, 파란색 성분을 설명하는 비트가 포함되어 있지만, 심도만으로 픽셀의 비트 순서(형식)가 지정되는 것은 아닙니다. 예를 들면 파란색, 녹색, 빨간색 순서이거나 그 역순일 수 입니다.

픽셀 색상 형식

애플리케이션의 색 심도에 따라서 몇 가지 사용할 수 있는 색상 형식이 있습니다.

RGB888

TouchGFX에서 색 심도가 24bpp인 색상의 형식은 RGB888입니다. 이 말은 빨간색, 녹색 및 파란색 성분마다 8비트가 사용된다는 것을 의미합니다.

이러한 색상, 예를 들어 밝은 보라색 rgb(255,0,255)를 코드로 표현하려면 아래와 같이 각 성분을 색상 값으로 조합해야 합니다.

uint32_t brightPurpleRGB888 = 255 << 16 | 0 << 8 | 255 << 0;

이 형식에서 빨간색은 최상위 8비트, 녹색과 파란색은 하위 8비트입니다.

RGB565

16bpp 색상에서 TouchGFX가 사용하는 색상 형식은 RGB565입니다. 즉, 빨간색은 5비트, 녹색은 6비트, 파란색은 5비트입니다. 빨간색이 5비트이면 완전히 밝을 때 31이므로 밝은 보라색의 코드는 아래와 같습니다.

uint16_t brightPurpleRGB565 = 31 << 11 | 0 << 5 | 31 << 0;

RGBx2222, xRGB2222, BGRx2222, xBGR2222

6bpp 색상에서는 TouchGFX가 4가지 다른 형식, 즉 RGBx2222, xRGB2222, BGRx2222, xBGR222를 지원합니다. 이러한 형식에 x가 있는 이유는 6비트 색상이 바이트로 저장되기 때문입니다. 색상은 2비트씩 채워져 1바이트를 형성합니다. 또한 RGB와 BGR을 모두 지원하는 이유는 일부 디스플레이에 BGR이 필요하더라도 이러한 디스플레이에 전송하기 전에 픽셀을 변환하지 않기 위해서입니다. 밝은 노란색을 RGBx2222로 표현하는 코드는 아래와 같습니다.

uint8_t brightYellowRGBx2222 = 3 << 6 | 3 << 4 | 0 << 2;

GRAY4, GRAY2, BW

TouchGFX는 그레이스케일 색상 심도에 따라 해당하는 한 가지 색상 형식을 지원합니다. 예를 들어 4bpp일 때 색상 형식은 GRAY4이고, 2bpp일 때는 GRAY2이며, 1bpp일 때는 흑백을 의미하는 BW입니다. 4bpp일 때 완전히 흰색은 아래와 같습니다.

uint8_t whiteGRAY4 = 15;

TouchGFX에는 색상 형식에 따라 정확한 색상 표현을 반환하는 헬퍼 함수가 포함되어 있습니다.

#include <touchgfx/Color.hpp>
...
aColor = Color::getColorFromRGB(255,0,128);

이미지 형식

이미지는 대부분의 UI 애플리케이션에서 매우 중요한 역할을 하며, 여러 가지 색상으로 채워집니다. TouchGFX에서는 이미지가 메모리에 저장되며, 특정 형식의 색상으로 채워집니다. 대부분의 경우 이미지는 지원되는 픽셀 색상 형식 중 하나를 사용하지만 그 밖에 다른 이미지 형식도 사용할 수 있습니다. 특정 이미지 색상 형식의 이미지에서 픽셀은 해당하는 픽셀 형식으로 변환된 후 그리기 작업을 하게 됩니다.

이미지 색상 형식표현
ARGB888832비트, 성분당 8비트
L8_ARGB88888비트 인덱스 형식, ARGB8888 팔레트
RGB88824비트, 성분당 8비트
L8_RGB8888비트 인덱스 형식, RGB888 팔레트
RGB66624비트, 성분당 6비트
RGB56516비트, 빨간색 5비트, 녹색 6비트, 파란색 5비트
L8_RGB5658비트 인덱스 형식, RGB565 팔레트
ARGB22228비트, 성분당 2비트
ABGR22228비트, 성분당 2비트
RGBA22228비트, 성분당 2비트
BGRA22228비트, 성분당 2비트
GRAY44비트 그레이스케일
GRAY22비트 그레이스케일
BW1비트 그레이스케일
BW_RLE1비트 그레이스케일 런 길이 인코딩(RLE)

위의 이미지 형식 중에서 L8 형식은 해당하는 이미지를 색상 룩업 테이블(CLUT)과 테이블에 대한 인덱스로 표현합니다. L8 이미지에서 가능한 최대 색상 수는 28개, 즉 256개입니다. L8 형식은 다른 형식에 비해 차지하는 공간이 비교적 적습니다. 예를 들어 100x100 이미지에 200가지 색상이 있다고 가정할 경우 ARGB8888 형식으로 저장하면 100x100x32비트, 즉 40000바이트의 공간을 차지하지만 L8_ARGB8888 형식으로 저장하면 100x100x8비트 + 200x32비트, 즉 10800바이트의 공간을 차지하게 됩니다. L8 형식 사용에 대한 자세한 내용은 여기에서 확인하십시오.

BW_RLE 형식은 단일 픽셀 색상이 아닌 연속된 흑백 이미지로 색상을 저장합니다. 이렇게 하면 대부분 경우 공간을 더욱 효율적으로 사용할 수 있습니다.

나머지 형식들은 위의 픽셀 색상 형식과 동일합니다.

프레임버퍼 형식

모든 이미지 형식을 프레임 버퍼 형식으로 사용할 수 있는 것은 아닙니다. L8 형식은 TouchGFX에서 프레임 버퍼 형식으로 사용할 수 없습니다. 왜냐하면 프레임 버퍼의 두 개 이미지에 형식을 혼합해 사용하는 것은 실용적이지 않기 때문입니다.

바이트 및 픽셀 순서

24비트 형식 RGB888과 32비트 형식 ARGB888은 종종 바이트 포인터를 사용해 액세스를 합니다. 이를 위해서는 픽셀이 리틀 엔디안 순서로 저장된다는 것을 이해해야 합니다.

32비트 색상 0xFFFF7700(알파 = 0xFF, 빨간색 = 0xFF, 녹색 = 0x77, 파란색 = 0x00)을 예로 들어보겠습니다. 색상이 32비트 변수 또는 레지스터에 있는 경우, 값은 0xFFFF7700이 됩니다. 색상이 메모리에 저장되어 있을 때는 저장된 바이트는 { 0x00, 0x77, 0xFF, 0xFF }가 됩니다. 이것은 순서 BGRA에 해당합니다.

마찬가지로 16비트 형식인 RGB565는 항상 16비트 포인터를 통해 액세스되기 때문에 바이트 순서가 그렇게 중요하지는 않지만, 어쨌든 메모리에서 스왑됩니다

8비트 형식의 경우, 예를 들어 ARGB2222 색상은 변경 없이 저장이 되기 때문에 1바이트(최상위 2개 비트의 알파)에 적합합니다.

크기가 더 작은 형식인 GRAY4, GRAY2 및 BW는 두 가지 순서로 저장할 수 있습니다. 하위 비트는 맨 왼쪽 픽셀이나 맨 오른쪽 픽셀이 될 수 있습니다. 하위 비트가 가장 왼쪽에 있으면 이를 LSB 모드라고 하고, 그렇지 않으면 MSB 모드입니다.

프레임버퍼 형식순서표현
ARGB8888BGRA32비트, 성분당 8비트
XRGB8888BGRX32비트, 성분당 8비트, 알파 바이트 무시
RGB888BGR24비트, 성분당 8비트
RGB56516비트, 빨간색 5비트, 녹색 6비트, 파란색 5비트
ARGB22228비트, 성분당 2비트
ABGR22228비트, 성분당 2비트
RGBA22228비트, 성분당 2비트
BGRA22228비트, 성분당 2비트
GRAY4LSB4비트 그레이스케일
GRAY2LSB2비트 그레이스케일
BWMSB1비트 그레이스케일

텍스트 형식

텍스트는, 더욱 엄밀히 얘기하면 글리프(glyph) 역시 특정 색상 형식으로 메모리에 저장됩니다. TouchGFX에서 사용할 수 있는 텍스트 색상 형식은 다음과 같습니다.

텍스트 색상 형식표현
A88비트, 불투명도 전용
A44비트, 불투명도 전용
A22비트, 불투명도 전용
A11비트, 불투명도 전용

글리프 형식은 작은 이미지와 비슷합니다. 여기에서는 색상 항목마다 각 픽셀의 불투명도가 저장됩니다. 이렇게 하면 실제 색상, 즉 빨간색, 녹색 및 파란색 성분을 나중에 적용할 수 있으며, 이를테면 저장된 글리프 ‘A’를 파란색 버전과 빨간색 버전으로 그리는 것도 가능합니다.

각 글리프마다 사용되는 비트 수가 많을수록 텍스트가 더욱 자연스럽고 선명하게 표시됩니다.

시각적 품질

임베디드 그래픽을 처리할 때는 시각적 품질을 최대한 높이고 싶은 마음이 당연하겠지만 메모리 사용량도 함께 고려해야 합니다.

따라서 색상이 더 풍부한 RGB888보다는 RGB565 색상 형식을 사용하는 것이 더 바람직합니다. 일반적으로 메모리 요건을 감안하여 시각적 품질을 최대한 높일 수 있는 색상 형식을 따르는 것이 좋습니다.

디더링

TouchGFX는 이미지를 여러 가지 색상 형식으로 표현할 때 시각적 품질을 높여주는 디더링이라고 하는 기법을 사용합니다.

디더링(Dithering)은 이미지의 색상을 실제보다 더 많아 보이게 만드는 기법으로 잘 알려져 있습니다. 이 기법은 이미지의 색상에 약간의 노이즈를 추가하는 방식입니다.

예를 들어 각 색상 성분에서 낮은 비트를 잘라내지 않고 RGB888 이미지를 RGB565 이미지로 변환한다고 가정할 경우 변환 프로세스에서 노이즈가 각 색상에 추가됩니다. 그러면 변환된 이미지의 색상이 더욱 풍부해져 원본인 RGB888과 비슷하게 보입니다.

아래 RGB888 원본 이미지와 변환된 이미지들을 가지고 이미지를 보면서 설명하겠습니다. 변환된 이미지의 형식은 각각 디더링을 적용한/적용하지 않은 RGB565와 xRGB2222, 그리고 GRAY4입니다.

RGB888 원본 이미지

디더링을 적용하고/적용하지 않고 변환된 RGB565 이미지

디더링을 적용하고/적용하지 않고 변환된 xRGB2222 이미지

디더링을 적용하고/적용하지 않고 변환된 GRAY4 이미지

위에서도 알 수 있듯이 디더링은 인지할 수 있는 이미지의 품질을 크게 개선하는 효과가 있습니다. 디더링이 적용된 RGB565 이미지와 그렇지 않은 이미지를 자세히 보면 디더링이 적용된 버전은 원본과 거의 차이가 없지만 디더링이 적용되지 않은 버전은 일부 영역에서 색상 밴딩 현상이 확연하게 보입니다. 이로써 대부분 경우 16비트 색상으로도 그래픽을 충분히 선명하게 표현할 수 있다는 것을 잘 보여주고 있습니다.

그래픽 애셋(asset)에 커다란 그래디언트(두 영역간 색의 변화도)가 있다면 디더링이 적용된 이미지에서도 색상 밴딩 현상이 나타날 수 있습니다. 여기 두 가지 예가 있습니다. 하나는 RGB888(64,190,222)에서 검은색에 이르는 파란색 그래디언트와 디더링을 적용하고/적용하지 않고 변환된 RGB565 이미지입니다.

RGB888 원본 이미지 그리고 디더링을 적용한 변환된 RGB565 이미지 그리고 디더링을 적용하지 않은 변환된 RGB565 이미지

또 하나는 (255,0,0)에서 검은색에 이르는 빨간색 그래디언트입니다.

RGB888 원본 이미지 그리고 디더링을 적용한 변환된 RGB565 이미지 그리고 디더링을 적용하지 않은 변환된 RGB565 이미지

자세히 보면 디더링이 적용된 RGB565 버전과 그렇지 않은 버전 모두에서 색상 밴딩 현상이 있다는 것을 알 수 있습니다. 이러한 밴딩 현상은 빨간색 이미지에서 가장 두드러집니다.

따라서 변환된 이미지와 색상 형식에 항상 주의를 기울이고, 필요할 경우 원본 이미지를 변경하거나 다른 색상 형식을 선택하는 것이 좋습니다.

성능

앞에서 언급한 이미지 형식은 모두 그리기 작업의 “용이성”에 최적화된 것입니다. 이 말은 별다른 변환 없이도 이미지를 프레임버퍼로 어느 정도 복사할 수 있다는 것을 의미합니다.

이는 의도적인 것으로, TouchGFX가 마이크로컨트롤러에서 자연스러운 그래픽을 구현할 수 있는 이유 중 하나입니다.

TouchGFX를 사용해 UI를 설계할 때는 .png 이미지를 사용하지만 이후 각 이미지는 위에서 자세히 설명한 것처럼 컴파일 단계에서 효율적인 이미지 형식 중 하나로 변환됩니다.

알파 블렌딩

런타임에서 이미지 데이터 복사는 일반적인 CPU 복사 작업을 통해, 혹은 MCU 기능을 사용해 이루어집니다. 이때 이미지에 완전히 투명하지 않거나 불투명한 픽셀이 포함되어 있으면 해당 픽셀을 배경에 알파 블렌딩 처리해야 합니다. 일부 STM32 MCU에서는 이러한 블렌딩 기능이 하드웨어를 통해 지원됩니다.

기타 이미지 형식

런타임에서 다른 이미지 형식, 예를 들어 .jpg나 .png 같은 압축 이미지 형식이 필요하다면 TouchGFX에서 지원되는 동적 비트맵 기능을 이용할 수 있습니다.

Further reading
색 심도에 대한 Wikipedia 글을 참조하십시오.