TL;DR
브랜치
예제 코드
이거 그려야지
2주차 과제는 thorvg.example 저장소를 포크뜨고 나만의 예제를 실행해보는 것이다.
과제를 보자마자 지인분이 그려준 개인 작업실에 걸려있는 그림을 모사해야겠다는 생각을 했다.
사무실에 액자에 모셔져있는 실제 광경을 보면 몹시 우스꽝스러워서 좋아하는 그림이다.
그림이 수수해 요구하는 api 난이도도 낮아보여서 선택한 이유도 있다.
어떻게 그리지
thorvg.example 저장소의 src 폴더 내 예제들을 보면 규격이 다 같다.
동일하게 사용되는 코드는 Example.h에 넣어두고 tvgexam::Example 구조체를 상속받아서 content 함수에서 thorvg api로 그림만 그리면 된다.
api 사용법은 thorvg 튜토리얼 과 저장소 내 다른 예제 코드를 참고하면 된다.
내 수준의 예제에서는
moveTo로 그릴 위치를 잡고
lineTo로 선을 긋고
cubicTo로 베지어 곡선을 그리고
fill로 색상 입히고
선은 stroke 접두어가 붙은 api들을 사용하면 된다.
시행 착오
우선 하얀 배경부터 만들어줬다.
auto background = tvg::Shape::gen();
background->appendRect(0, 0, w, h);
background->fill(255, 255, 255);
canvas->add(background);
그리고 불꽃을 만들기 시작했는데 여기서 부터 사실 망했다.
bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
{
auto background = tvg::Shape::gen();
background->appendRect(0, 0, w, h);
background->fill(255, 255, 255);
canvas->add(background);
auto flame = tvg::Shape::gen();
flame->moveTo(597, 357);
flame->lineTo(579, 351);
flame->lineTo(564, 350);
flame->lineTo(575, 356);
flame->lineTo(548, 357);
flame->lineTo(534, 364);
flame->lineTo(548, 367);
flame->lineTo(525, 375);
flame->lineTo(513, 383);
flame->lineTo(527, 383);
flame->lineTo(505, 391);
flame->lineTo(497, 400);
flame->lineTo(513, 400);
flame->strokeFill(255, 0, 0);
flame->strokeWidth(2);
flame->strokeCap(tvg::StrokeCap::Round);
canvas->add(flame);
return true;
}
위 코드를 실행하면 아래 그림이 된다.
저 적절한 좌표들 찾느라 살짝 과장보태서 2시간은 쓴거같다. 용가리 불꽃 하나 그리려다 세월이 다 가게 생겼다.
직접 손으로 작성하려다가 여기서부터 포기하고 좌표는 그냥 ai의 도움을 받았다.
마음에 안드는 좌표나 마감을 살짝 다듬으니 그럭저럭 적당히 모사가 되었다. (눈 부분을 더 칠해주려다가 이쯤에서 멈춤)
코드
bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
{
auto background = tvg::Shape::gen();
background->appendRect(0, 0, w, h);
background->fill(255, 255, 255);
canvas->add(background);
drawCastle(canvas);
drawDragon(canvas);
drawFlame(canvas);
drawKnight(canvas);
return true;
}
lineTo, moveTo 같은 단순 api 나열을 보면 의미가 와닿지 않아서 drawCastle 과 같은 함수로 의도가 드러나도록 했다.
void stroke(tvg::Canvas* canvas, tvg::Shape* shape, uint8_t r, uint8_t g, uint8_t b, float width)
{
shape->strokeFill(r, g, b);
shape->strokeWidth(width);
shape->strokeCap(tvg::StrokeCap::Round);
canvas->add(shape);
}
strokeFill : 선을 칠하고, strokeWidth : 선 너비를 정하고, strokeCap : 선 양끝을 마감하는 반복되는 과정도 stroke 함수로 빼준다.
void addWhiteShape(tvg::Canvas* canvas, tvg::Shape* shape)
{
shape->close();
shape->fill(255, 255, 255);
shape->strokeFill(18, 18, 18);
shape->strokeWidth(2.5f);
shape->strokeCap(tvg::StrokeCap::Round);
canvas->add(shape);
}
위와 마찬가지로 하얀색으로 덮는 부분의 코드도 반복 사용되어 addWhiteShape 함수로 작성했다.
후기
구현상의 api 난이도가 낮아서 오히려 처음 작성해보기에는 적합하지 않았나 생각했는데… 앞서 제출하신 분이 너무나도 멋진 예제를 작성하셔서 살짝 위축이 되었다..
그래도 thorvg에 조금은 친숙해진 시간이 된 것 같다!
댓글
Discussion 원문