커스텀 위젯
애플리케이션을 생성할 때 TouchGFX 배포판에 포함되지 않은 위젯이 필요할 수도 있습니다. TouchGFX에는 사용자가 자신만의 그래픽 요소를 생성할 수 있는 몇 가지 방법이 있습니다. 가장 손쉬운 방법은 커스텀 컨테이너 방식을 사용하여 기존 위젯을 조합하여 사용자 고유의 위젯을 생성하는 것입니다. 하지만 이 섹션에서는 고급 접근 방식으로서, 프레임버퍼를 완전히 제어할 수 있는 위젯을 생성하여 원하는 것을 정밀하게 그리는 방법을 알아보겠습니다.
커스텀 위젯 접근 방식을 사용해야 하는 경우
커스텀 위젯 생성은 사용자 고유의 위젯을 생성하는 작업으로서 가장 일반적인 방법은 아닙니다. 요건에 부합한다면 커스텀 컨테이너 접근 방식을 선호하지만 이 방법만으로는 부족한 경우도 있습니다. 프레임버퍼를 완전히 제어하려면 커스텀 위젯 접근 방식을 사용해야 합니다.
커스텀 위젯 접근 방식을 사용해 위젯을 생성할 수 있고, 생성해야 하는 예를 들면 다음과 같습니다.
- 세피아(Sepia) 필터
- 만델브로 프랙탈(Mandelbrot fractal) 위젯
- 파일의 데이터를 표시하는 위젯(예: gif 애니메이션 등)
TouchGFX Designer에서 커스텀 위젯 생성하기
TouchGFX Designer는 현재 커스텀 위젯 생성을 지원하지 않습니다. 따라서 커스텀 위젯 코드를 직접 작성한 후(작성 방법은 본 섹션의 나머지 부분 참조), View에서 사용자 코드 구간에 위젯을 삽입해야 합니다.
코드에서 커스텀 위젯 생성하기
사용자 고유의 커스텀 위젯은 Widget
클래스를 확장하여 생성합니다. 이때 사용자의 수작업이 다소 필요하지만, 위젯에서 그려지는 모든 픽셀들을 완벽하게 제어할 수 있습니다. 커스텀 위젯이 반드시 기존 위젯을 이용할 필요는 없지만 픽셀의 색상을 지정하여 그리는 방식을 정의합니다. 커스텀 위젯 접근 방식은 일반적으로 차지하는 메모리 공간이 비교적 적어서 경우에 따라 매우 중요하게 사용되기도 합니다.
커스텀 위젯의 한 가지 예로 QR 코드 위젯이 있습니다. QR 코드 위젯이 대표적인 예이므로 다음 섹션에서는 이 위젯을 생성하는 방법을 알아보겠습니다. 이 예시에서 QR 코드 위젯은 흑백 픽셀의 NxN 매트릭스입니다. 위젯의 크기와 각 픽셀의 색상은 런타임에서 결정되며, QR 코드 데이터 객체의 정보에 따라 달라집니다.
다음은 QR 코드 위젯의 형태를 나타낸 두 가지 예시입니다.
Caution
Tip
touchgfx::Button
메소드(프레임워크와 함께 제공됨)는 커스텀 컨테이너가 아닌 커스텀 위젯으로 생성됩니다. 따라서 스크린에서 버튼별 메모리 점유량을 줄일 수 있습니다.사용자 고유의 커스텀 위젯
Widget
클래스를 확장하는 위젯을 생성하려면 두 가지를 설명해야 합니다.
- 위젯이 그려지는 방법
- 위젯에서 불투명한(solid) 영역
부분 그리기
커스텀 위젯을 포함해 모든 위젯은 부분 그리기를 지원해야 합니다. 이 말은 위젯이 일부분만 그릴 수 있어야 함을 의미합니다.
이는 두 가지 사항에서 기인하는데, 하나는, 스크린 전체를 다시 그리는 것 보다는 일부분만 그려야 하는 경우가 많기 때문이며 나머지 하나는, TouchGFX의 알고리즘은 그려지는 전체 픽셀 수의 최소화를 위해 스크린의 그림을 여러 부분의 그림으로 분리할 수 있기 때문입니다. 이러한 경우에는 스크린의 부분 그리기는 위젯에게 부분적으로만 그리도록 요청할 수 있습니다.
예를 들어 QR 위젯 역시 다음과 같이 강조된 영역만 그리는 기능을 지원할 수 있어야 합니다.
코드에서 부분 그리기를 수행하려면 다음과 같이 draw
메소드를 재정의하면 됩니다.
virtual void draw(const touchgfx::Rect& invalidatedArea) const
{
//run through the pixels of the invalidated area
//draw a black pixel if the qr data object has a value at this position
//draw a white pixel otherwise
}
invalidatedArea
는 위젯에서 업데이트가 필요한 영역입니다. QR 코드 예에서 강조된 영역이 무효화 영역에 해당합니다. 이 영역은 위젯의 왼쪽 상단 모퉁이를 기준으로 좌표로 표현됩니다.
Caution
Tip
draw
메소드는 const입니다.
최적화된 그리기 알고리즘이 위젯 그리기를 다수의 그리기 호출로 분할할 수 있기 때문입니다. 위에서 const입니다.
이렇게 다수의 draw
메소드를 실행하는 도중 위젯이 움직이거나 업데이트되지 않도록 보장합니다.불투명한 영역
또한 위젯은 불투명한 영역이 얼마나 큰지 보고할 수 있어야 합니다. 여기에서 불투명이란 스크린에서 위젯에 가려진 부분을 볼 수 없다는 것을 의미합니다. 예를 들어 빨간색 표준 박스는 완전 불투명인 반면 알파 값이 50%인 이미지는 완전 불투명이 아니기 때문에 가려진 부분을 모두 볼 수 있습니다.
불투명한 위젯은 TouchGFX에서 더욱 빠르게 그릴 수 있습니다. 불투명한 영역 아래로는 위젯을 그릴 필요가 없기 때문에 그려야 할 픽셀이 비교적 적습니다.
코드에서 불투명한 영역을 보고하려면 getSolidRect
메소드를 재정의합니다.
virtual Rect getSolidRect() const;
그러면 QR 코드 위젯이 완전히 불투명해집니다. 결국 흑백 픽셀에 가려진 부분은 아무것도 볼 수 없게 됩니다. 따라서 다음과 같이 메소드가 실제 위젯 크기로 직사각형을 반환합니다.
virtual Rect getSolidRect() const
{
return touchgfx::Rect(0, 0, getWidth(), getHeight());
}
소스 코드 예제
결과적으로 제공되는 데이터에 따라 다음과 같은 애플리케이션과 위젯이 생성됩니다.
전체 위젯 코드는 아래와 같습니다.
gui/include/gui/common/QRCodeWidget.hpp
#ifndef QR_CODE_WIDGET_HPP
#define QR_CODE_WIDGET_HPP
#include <touchgfx/widgets/Widget.hpp>
class QRCodeWidget : public touchgfx::Widget
{
public:
QRCodeWidget();
virtual void draw(const touchgfx::Rect& invalidatedArea) const;
virtual touchgfx::Rect getSolidRect() const;
void setQRCodeData(QRCodeData* data);
void setScale(uint8_t s);
private:
void updateSize();
QRCodeData* data;
uint8_t scale;
};
#endif
헤더 파일에서 위젯을 touchgfx::Widget
클래스의 확장으로 정의합니다. draw
메소드와 getSolidRect
메소드를 재정의하여 위젯의 그리기 방식을 정의합니다. QR 코드의 데이터를 설정하는 메소드와 QR 코드의 스케일링 계수를 설정하는 메소드를 선언합니다.
gui/src/common/QRCodeWidget.cpp
#include <gui/common/QRCodeWidget.hpp>
QRCodeWidget::QRCodeWidget() :
data(0),
scale(1)
{
}
void QRCodeWidget::draw(const touchgfx::Rect& invalidatedArea) const
{
if (!data)
{
return;
}
touchgfx::Rect absolute = getAbsoluteRect();
uint16_t* framebuffer = touchgfx::HAL::getInstance()->lockFrameBuffer();
for (int y = invalidatedArea.y; y < invalidatedArea.bottom(); y++)
{
for (int x = invalidatedArea.x; x < invalidatedArea.right(); x++)
{
framebuffer[absolute.x + x + (absolute.y + y) * touchgfx::HAL::FRAME_BUFFER_WIDTH] =
data->at(x / scale, y / scale) ? 0x0000 : 0xffff;
}
}
touchgfx::HAL::getInstance()->unlockFrameBuffer();
}
touchgfx::Rect QRCodeWidget::getSolidRect() const
{
return touchgfx::Rect(0, 0, getWidth(), getHeight());
}
void QRCodeWidget::setQRCodeData(QRCodeData* qrCode)
{
data = qrCode;
updateSize();
}
void QRCodeWidget::setScale(uint8_t s)
{
scale = s;
updateSize();
}
void QRCodeWidget::updateSize()
{
if (data)
{
setWidth(data->getSize() * scale);
setHeight(data->getSize() * scale);
}
}
소스 파일에서는 draw
메소드를 정의합니다. 이 메소드는 무효화 영역의 각 픽셀을 확인한 후 데이터 객체의 내용과 스케일링 계수에 따라 프레임버퍼의 색상을 결정합니다.
getSolidRect
메소드는 위젯이 완전히 불투명한 것으로 보고합니다.
Caution
draw
메소드에서 프레임버퍼를 잠근 후 다시 잠금 해제했습니다. 이유는 그리기를 시작할 때 DMA를 사용해 그리기를 마치기 위해서입니다.코드 역시 작은 클래스/인터페이스를 사용해 QR 코드 데이터에 액세스합니다.
class QRCodeData
{
public:
uint8_t getSize();
bool at(uint8_t x, uint8_t y);
};
Things to try
표준 위젯/컨테이너 수정하기
TouchGFX를 설치하면 표준 위젯의 소스 코드도 함께 제공됩니다. 이들 코드는 애플리케이션의 요건에 맞게 수정할 수도 있습니다. 표준 위젯과 컨테이너의 소스 코드는 아래 폴더에 있습니다.
Middlewares\ST\touchgfx\framework\source\touchgfx\widgets
코어 라이브러리에는 하위 호환성을 유지하기 위해 표준과 컨테이너의 컴파일 버전이 포함되어 있습니다. 따라서 프로젝트에서 이러한 파일을 컴파일할 필요가 없습니다.
Caution
소스 코드는 기본적으로 아이디어를 제공하는 동시에 TouchGFX 위젯의 내부 실행 알아보는 데 목적이 있습니다. 표준 구현체와 다른 동작을 원한다면 커스텀 컨테이너를 하위 분류하거나 생성하여 원하는 동작을 구현할 수 있으며, 이 역시 권장하는 방법입니다.
이러한 방식을 권장하는 이유는 두 가지입니다.
- 첫째, 사용자 변경 사항은 사용자가 직접 병합해야 하므로 최신 TouchGFX 버전으로 업그레이드하기가 더욱 어려워집니다.
- 둘째, 특정 동작에 따라 표준 위젯과 컨테이너가 잘못될 위험이 있습니다.
따라서 표준 위젯/컨테이너를 수정해야 한다면 소스 코드를 직접 변경하지 말고 먼저 복사하여 이름을 변경한 후 사용하는 것이 좋습니다.
그렇기는 해도 옳다고 판단하는 작업이면 무엇이든 자유롭게 수행할 수 있습니다. 표준 위젯의 소스 코드를 프로젝트에 추가하면 링커가 코어 라이브러리에 있는 버전이 아닌 이 버전을 선택하게 됩니다. 결과적으로 소스 코드를 컴파일 버전에 추가하여 표준 위젯을 수정할 수 있습니다.