SVG
TouchGFX는 버전 4.21부터 SVG 이미지 사용을 지원합니다. SVG 이미지는 기존의 비트맵 기반 그래픽을 벡터 그래픽과 하나로 결합해 사용자 인터페이스를 생성하는 데 사용할 수 있습니다.
SVG 이미지는 SVGImage 위젯을 통해 사용자 인터페이스에 포함이 됩니다. 이 위젯은 TouchGFX Designer에서 사용할 수 있으며, 다른 위젯처럼 사용자 인터페이스에 추가할 수 있습니다.
SVG 이미지는 16bpp 또는 24bpp 프레임 버퍼에서만 지원됩니다.
SVG 소개
SVG(Scalable Vector Graphics)는 소규모의 그래픽 프리미티브 세트를 사용해 2D 이미지를 정의하기 위한 XML 기반 파일 형식입니다. 프리미티브 중 일부는 직사각형, 원 및 곡선입니다. 프리미티브는 색상이나 그라데이션으로 채울 수 있습니다. 특정 너비로 외곽선(스트로크)을 그리는 것도 가능합니다.
SVG 그래픽의 가장 큰 특징은 확장이 가능하다는 것입니다. 즉, 지정된 그리기 작업을 단일 해상도로 사용할 수 있을 뿐만 아니라, 품질 저하 없이 확대 또는 축소할 수 있습니다. 확대할 때는 아티팩트가 사용되고 축소할 때는 세부 정보가 손실되는 Bitmap과 비교해 볼 때 큰 이점이 아닐 수 없습니다.
SVG 그래픽의 두 번째 주요 특징은 벡터 기반이라는 것입니다. 즉, 그리기 작업을 하기 위해 하나로 결합되는 이미지는 선과 원 같이 일련의 기하학적 도형으로 이루어져 있습니다. 기본적으로 이미지의 모든 픽셀에 대한 색상 값을 정의하는 비트맵 이미지와는 대조되는 부분입니다. 벡터 정의의 장점은 대부분의 경우 이미지가 비트맵에 비해 매우 작고 훨씬 더 유연하다는 것입니다. 예를 들어 몇 개의 노란색 타원을 표시하는 이미지를 녹색 타원으로 이루어진 이미지로 쉽게 바꿀 수 있습니다.
TouchGFX는 SVG Tiny 1.2의 하위 집합을 지원합니다. TouchGFX의 하드웨어, 런타임 환경 및 성능 특성으로 인해 부과되는 제한 조건 내에서 전체 사양을 지원하는 것은 불가능합니다.
다양한 이미지 도구를 사용해 SVG 이미지를 생성할 수 있으며, 손으로 직접 작성할 수도 있습니다. 자체 SVG를 작성하는 방법은 여기 소개 부분을 읽어볼 것을 권장합니다.
TouchGFX에서 SVG 사용하기
SVG 이미지는 PNG 이미지와 같은 방식으로 사용됩니다.. 프로젝트에서 사용하려는 SVG 파일이 assets/images 폴더에 있어야 합니다. TouchGFX Image Converter 및 TouchGFX Designer는 해당 폴더에 나타나는 이미지를 읽습니다.
SVG 이미지는 CPP 코드로 변환되어 애플리케이션에 연결됩니다. 이 CPP 코드는 generated/images/src/SVGDatabase.cpp
파일에 저장됩니다.
SVG 이미지는 코드에서, 또는 TouchGFX Designer에서 SVGImage 위젯과 함께 사용됩니다. 이 위젯을 사용하면 SVG 이미지의 확대/축소, 변환 및 회전이 가능합니다.
SVGImage 위젯은 또 다른 구성 요소인 VectorRenderer를 사용해 렌더링을 수행합니다.
예제
예를 들어 다음 SVG를 사용하겠습니다.
<?xml version="1.0" encoding="iso-8859-1"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="green" />
<polygon points="50,10 78,78 22,78" fill="gold" />
</svg>
SVG는 반지름이 40인 진한 녹색 원과 노란색(황금색) 삼각형으로 이루어져 있습니다.
보시다시피 SVG 이미지는 일반적인 텍스트 편집기를 사용해 수정할 수 있는 간단한 텍스트 파일입니다. 다양한 표준 도구를 사용해 SVG 파일을 열 수 있습니다. SVG 뷰어의 한 예로 Chrome 브라우저가 있습니다.
이 SVG의 렌더링은 다음과 같이 이루어집니다.
이 SVG를 TouchGFX와 함께 사용하기 위해 파일(arrow.svg)을 프로젝트의 assets/images
디렉토리에 저장합니다. 이것은 TouchGFX Designer에서 사용하는 PNG 파일 및 이미지와 동일한 디렉토리입니다.
TouchGFX Designer에서 SVGImage 위젯을 삽입할 수 있습니다.
오른쪽에 있는 SVGImage의 속성을 사용해 위젯의 크기와 스케일을 변경할 수 있습니다.
"Fit Image To Size" 확인란에 유의하십시오. 확인란을 선택하면 위젯의 크기에 맞게 SVG가 확대/축소되고 회전이 재설정 됩니다. 원하는 크기가 될 때까지 위젯의 모서리를 드래그해서 SVG 크기를 조정할 수 있도록 도와주는 기능입니다.
위와 같이 회전을 하기 위해서는 회전 중심을 설정하는 것이 중요합니다. 기본값은 x=0, y=0이며, 이는 위젯의 왼쪽 상단 모서리에 해당합니다. 이 회전 중심을 사용하고 SVG를 회전하면 아래 그림에서와 같이 SVGImage 위젯의 왼쪽까지 시계 방향으로 회전합니다.
위 그림에 나와 있듯이, SVGImage 외부에 있는 SVG 부분은 잘립니다(표시되지 않음). 회색 사각형은 SVGImage를 나타냅니다. 회색 영역 밖에 있는 것은 그려지지 않습니다. SVGImage 내에서 SVG를 회전하려면 회전 중심을 위젯의 중심(50, 50)으로 설정해야 합니다.
뒤에 나올 섹션에서 변환의 자세한 내용을 확인하십시오.
대상 구성
대상에서 SVG 이미지를 사용하려면 CubeMX에서 벡터 렌더링이 활성화되어 있어야 합니다. 이렇게 하면 SVGImage에 필요한 벡터 렌더러를 생성하기 위해 프로젝트에 필요한 코드가 삽입됩니다.
Note
벡터 렌더링이 활성화되지 않은 경우, 프로젝트를 빌드할 때 링커 오류(undefined reference to `touchgfx::VectorRenderer::getInstance())가 발생합니다.
이 문제는 CubeMX에서 구성을 변경하면 해결됩니다. 지침은 TouchGFX Generator 사용자 가이드를 참조하십시오.
SVG 이미지가 C++ 코드 형태로 대상 프로젝트에 자동으로 포함되어 있기 때문에 추가 작업이 필요하지 않습니다.
SVG 이미지는 비트맵보다 렌더링 시간이 상당히 길기 때문에 이중 버퍼링이나 임베디드 GRAM을 탑재한 디스플레이에서의 단일 버퍼링을 사용하는 것이 좋습니다. 단일 프레임 버퍼를 사용할 수는 있지만, 렌더링 시간과 디스플레이 티어링을 모니터링해야 합니다. 단일 프레임 버퍼를 사용하는 자세한 방법은 여기를 참조하십시오.
캔버스 버퍼
대부분의 플랫폼(U5X9 제외)에서 SVG는 캔버스 위젯 렌더러를 사용해 렌더링이 됩니다. 이 소프트웨어 구성 요소는 렌더링 중에 버퍼를 사용합니다. 이 버퍼의 크기는 성능에 영향을 미칩니다. 가능하면 버퍼 크기를 늘리는 것이 좋습니다(종종 20-30Kb가 적당). 버퍼는 TouchGFX Designer에서 손쉽게 변경이 됩니다.
모든 관련 화면에서 반드시 값을 변경해야 합니다.
목표 성능 및 플래시 사용량
화살표에 대해 생성된 SVG 데이터의 크기는 350바이트 미만입니다. 비트맵에 비해 SVG 사용이 이점인 이유 중 하나가 여기에 있습니다. 100x100 크기에서 비트맵 형태의 화살표 크기는 24비트 색상에서 30.000바이트가 됩니다. 따라서 SVG의 크기는 비트맵 크기의 1%에 가깝습니다.
이미지 유형 | 플래시 사용량 |
---|---|
비트맵 | 30.000바이트 |
SVG | 350바이트 |
SVG 이미지를 사용하면 애플리케이션에 5-10,000개의 코드가 추가됩니다.
SVG를 사용할 때 단점은 SVG 이미지 그리기 성능이 대부분의 경우에 비트맵보다 낮다는 것입니다. SVG의 렌더링은 SVG의 요소 개수에 따라 달라지며, 상한선이 없습니다. 비트맵의 렌더링은 해상도와 알파 블렌딩에 따라 다르며 상한선이 있습니다.
두 번째로 SVG 이미지는 소프트웨어 렌더링을 많이 사용해서 렌더링이 되기 때문에 CPU 부하가 높습니다. 반면에, 비트맵은 CPU 부하가 낮은 DMA2D 가속기가 독점적으로 렌더링할 수 있습니다.
SVG 화살표의 렌더링 시간은 STM32F746(외부 RAM의 프레임 버퍼)에서 1.32ms이기 때문에 매우 유용합니다. H7 시리즈의 MCU는 속도가 더 빠르기 때문에 렌더링 시간이 단축됩니다. 회전이 크게 추가되지 않는 상태에서 SVG가 확대(렌더링할 픽셀이 증가)되면 렌더링 시간이 늘어납니다. 이 그림은 STM32F746 디스커버리 키트의 화살표를 보여줍니다.
맨 왼쪽 그림에는 SVG가 보이지 않고 녹색 상자 배경만 있습니다. 가운데 그림은 눈금 1의 화살표를 보여줍니다. 맨 오른쪽 그림은 눈금 2의 화살표를 보여줍니다. 전체 프레임에서의 렌더링 시간과 SVG에서만의 렌더링 시간은 다음과 같습니다.
화면 | 렌더링 시간 | SVG에서만 |
---|---|---|
왼쪽 | 2.96ms | 해당 없음 |
중간 | 4.28ms | 1.32ms |
오른쪽 | 6.37ms | 3.41ms |
회전 | 6.53ms | 3.57ms |
마지막 행은 화살표가 77도 회전되었을 때의 렌더링 시간을 보여줍니다. 회전이 렌더링 시간에 미미한 영향을 미친다는 사실을 알 수 있습니다.
그래픽 품질
이 그림은 STM32F746 디스커버리 키트에서 실행 중인 프로젝트를 보여줍니다.
이 프로젝트에서는 여러 SVGImage 위젯에서 동일한 SVG를 사용했습니다.
사진의 오른쪽 크기에 프레임 버퍼에서 확대된 세부 정보(3개의 화살촉)를 삽입했습니다. 이는 회전 및 확대/축소 시에도 SVG 그리기의 선이 모두 앤티앨리어싱 처리가 되었음을 보여줍니다. 이렇게 회전 및 확대/축소가 수행되는 동안 품질이 보존된다는 것이 SVG 이미지를 사용하는 주된 이유입니다
지원되는 SVG 요소 지원
TouchGFX는 SVG Tiny 1.2 표준 전체를 구현하지 않습니다. .svg 파일을 읽는 TouchGFX의 이미지 변환기는 지원되지 않는 요소나 속성을 사용하는 경우에 경고를 표시합니다. 중요 누락 부분은 방사형 그래디언트, 텍스트 및 애니메이션입니다.
TouchGFX는 다음과 같은 SVG 요소를 지원합니다. 예제는 요소에서 지원되는 속성을 보여줍니다.
Rectangle <rect x="10" y="20" width="100" height="200" rx="5" ry="40" />
Circle <circle cx="100" cy="200" r="20" />
Ellipse <ellipse cx="100" cy="200" rx="20" ry="30" />
Line <line x1="10" y1="10" x2="300" y2="150 />
Path <path d="M100 100L75 200h50z" />
Polygon <polygon points="10,10 30,30 10,90" />
Polyline <polyline points="10,10 30,30 10,90" />
LinearGradient <lineargradient id="grad1" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="100%" />
아래의 구조적 요소들도 지원됩니다.
- Group <g />
- Defs <defs />
TouchGFX는 행렬, 변환, 확대/축소, 회전, skewX, skewY 등 모든 모양 변환을 지원합니다.
선형 그래디언트는 <defs> 요소 내에서 정의가 되어야 합니다.
<path> 요소에서 TouchGFX는 M, m, L, l, H, h, V, v, C, c, S, s, Q, q, T, t, A, a 등 모든 경로 명령을 지원합니다.
채우기와 스트로크가 지원됩니다.
지원되지 않는 SVG 요소
TouchGFX에서 지원되지 않는 요소는 다음과 같습니다.
- Animations <animate>, <animateColor>, <animateMotion>, <animateTransform>, <discard>, <handler>, <listener>, <mpath>, <script>, <set>, <switch>
- Audio <audio />
- ClipPath <clipPath id=.... />
- Desc <desc />
- Filter <filter>
- Foreign Object <foreignObject>
- Gradient transform <linearGradient gradientTransform="..." />, <prefetch>
- Radial gradient <radialGradient ... />
- Solid color <solidColor>
- Stroke dasharray property <path stroke-dasharray="10,10" ... />
- Text and fonts <font>, <glyph>, <text>, <hkern>, <missing-glyph>, <tbreak>, <textArea>, <tspan>
- Use <use>
- Image <image>
- Video <video>
메타 데이터와 제목 요소는 무시됩니다.
ViewBox
SVG 이미지에서 자주 사용되는 viewBox 속성은 TouchGFX에서 지원되지 않습니다. viewBox 속성은 SVG 그리기(사용자 공간)에서 사용되는 좌표계를 SVG가 그려지는 뷰 포트로 변환하는 것을 정의하는 데 사용됩니다.
다음은 하나의 예입니다.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
...
</svg>
따라서, 직사각형에서 이 SVG가 (0,0) ~ (200,200)까지 그려진 모든 그래픽은 보기(SVGImage 위젯)에 맞게 늘이거나 줄여야 합니다.
TouchGFX에서 viewBox 속성은 무시됩니다. 이는 그래픽이 확대/축소되지 않음을 의미합니다. 그래픽이 올바른 스케일로 그려졌기 때문에 많은 SVG 이미지에서 이것은 문제가 되지 않습니다. 이것이 사실이라면 viewBox 속성을 그대로 두거나 삭제할 수 있습니다.
그래픽 역시 viewBox에 따라 잘리지 않고 SVGImage 위젯에 따라 잘립니다.
특정 그리기를 확대/축소하기 위해 viewBox가 실제로 필요한 경우, 추가 <g> 요소에서 변환을 삽입해서 SVG 파일에서 확대/축소를 적용하거나 TouchGFX에서그리기를 확대/축소할 수 있습니다.
다음은 (0,0)에서 (1000,1000)까지의 선이 viewBox(작동하지 않음)를 통해 (0,0)에서 (100,100)까지 축소되는 예입니다.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 1000 1000">
<path d="M10 10L990,990" stroke="blue" stroke-width="10" stroke-linecap="round"/>
</svg>
의도한 결과는 (0,0)에서 (200,200)까지의 선입니다. 그러나 TouchGFX는 ViewBox를 무시하므로 1000x1000픽셀의 SVG 이미지를 얻게 됩니다. 이를 변환하는 한 가지 방법은 스케일 변환을 포함하도록 SVG 파일을 수정하는 것입니다.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<g transform="scale(0.2)">
<path d="M10 10L990,990" stroke="blue" stroke-width="10" stroke-linecap="round"/>
</g>
</svg>
이러한 변환은 모든 것을 5배까지 축소합니다.
SVGImage 위젯에 확대/축소를 적용해서 이러한 효과를 얻을 수도 있습니다.
어쨌든 애플리케이션에서 확대/축소를 하는 경우에는 .svg 파일을 수정하지 않는 것이 좋습니다.
선형 그래디언트는 ObjectBoundingBox를 사용해야 합니다.
SVG의 선형 그래디언트는 두 가지 좌표계를 사용해 지정할 수 있습니다. "userSpaceOnUse" 또는 "objectBoundingBox"가 바로 그것입니다. TouchGFX에서는 "objectBoundingBox"만 지원됩니다. 여기서는 "objectBoundingBox"를 사용하는 방법을 간략하게 알아보겠습니다. 따라서 "userSpaceOnUse"를 사용하는 모든 SVG를 "objectBoundingBox"로 변경할 수 있습니다. 예제부터 시작해 보겠습니다.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
<rect x="130" y="10" width="40" height="80" fill="url(#gradient1)" />
</svg>
여기서는 처음에는 파란색이었다가 중간에는 녹색으로, 결국은 빨간색으로 바뀌는 그래디언트를 정의합니다. 그래디언트는 원과 사각형을 채우는 데 사용됩니다.
그래디언트의 시작점은 (x1, y1) = (0%, 0%)으로 지정이 됩니다. 이는 이 그래디언트로 페인팅된 모든 모양(예: 원 또는 직사각형)의 바운딩 박스의 왼쪽 상단 모서리를 나타냅니다. 마찬가지로 바운딩 박스의 오른쪽 하단 모서리가 끝점이 됩니다.
원과 사각형 두 도형은 너비(80픽셀과 40픽셀)가 다르기 때문에 모양에 적용했을 때 그래디언트의 각도가 다릅니다. 이 동작을 원하지 않으면 여러 그래디언트를 정의할 수 있는데, 각 그림마다 하나씩 나와 있습니다.
SVG에서는 "userSpaceOnUse" 좌표계를 사용하는 것도 가능합니다.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
<rect x="130" y="10" width="40" height="80" fill="url(#gradient1)" />
</svg>
이렇게 하면 다른 렌더링(TouchGFX에서 지원되지 않음)이 생성됩니다.
이것이 다르게 보이는 것은 두 모양 모두에서 그래디언트의 시작점이 전체 이미지의 왼쪽 상단 모서리이기 때문입니다(두 모양에 모두 적용).
userSpaceOnUse를 사용하는 SVG가 있는 경우, 그래디언트를 사용 중인 모양을 기준으로 그래디언트의 시작점을 계산해서 objectBoundingBox로 이를 변환할 수 있습니다. 다음은 그래디언트가 픽셀 좌표와 함께 userSpaceOnUse를 사용해서 원의 색상을 지정하는 예입니다.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" gradientUnits="userSpaceOnUse" x1="10" y1="10" x2="90" y2="90">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
</svg>
원의 바운딩 박스를 계산해서 objectBoundingBox를 사용하도록 그래디언트를 변환할 수 있습니다. (10,10)부터 (90,90)까지가 기울기와 일치합니다. 따라서 "10"을 "0%"로, "90"을 "100%"로 교체해서 좌표를 상대적인 모양 좌표로 변환할 수 있습니다.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="blue" />
<stop offset="50%" stop-color="green" />
<stop offset="100%" stop-color="red" />
</linearGradient>
</defs>
<circle cx="50" cy="50" r="40" fill="url(#gradient1)" />
</svg>
"%"를 기억하십시오. y1="10"이란 y1="1000%"임을 의미합니다.
ouchGFX Image Converter 경고
지원되지 않는 요소나 속성을 사용하면 TouchGFX Image Converter가 경고 또는 오류를 인쇄합니다. 예를 들어 화살표의 텍스트를 입력할 수 있습니다.
<?xml version="1.0" encoding="iso-8859-1"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="green" />
<polygon points="50,10 78,78 22,78" fill="gold" />
<text x="38" y="78">ABC</text>
</svg>
TouchGFX Designer는 .svg 파일에서 TouchGFX Image Converter를 실행하고 오류를 표시합니다.
이 오류 출력은 오류가 포함된 SVG 파일과 문제가 발견된 줄, 지원되지 않는 요소가 사용된 줄을 보여줍니다.
관련 문서
위에서 언급한 바와 같이, 대상 프로젝트에 대한 벡터 그래픽 지원은 TouchGFX Generator에서 구성이 됩니다. 지침은 TouchGFX Generator 사용자 가이드를 참조하십시오.
SVGImage 위젯은 TouchGFX Designer에서 사용이 가능합니다. TouchGFX Designer에서 SVGImage 위젯을 사용하는 자세한 방법은 여기를 참조하십시오.