You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
두 LottieFiles 저장소를 처음 보면 dotlottie-web이 웹 플레이어이고 dotlottie-rs는 별도의 Rust 구현처럼 보인다. 하지만 실제 구조는 dotlottie-web이 WASM으로 빌드된 dotlottie-rs를 사용하고, 다시 dotlottie-rs가 ThorVG를 실제 렌더러로 사용하는 형태다.
dotlottie-web
↓ WebAssembly
dotlottie-rs
↓ ThorVG C API
ThorVG
이 글에서는 .lottie 파일이 브라우저에서 로드된 뒤 어떻게 ThorVG에 전달되고, 어떻게 화면에 출력되는지 알아볼 것이다.
Note
Web 외에도 Android, iOS, Flutter, React Native용 dotLottie player가 존재한다. 이 글에서는 Web 환경을 기준으로 설명하지만, 구조와 역할은 다른 플랫폼에서도 유사하다.
0. dotLottie란 무엇인가?
dotLottie: An open-source animation package format for every platform
LottieFiles는 Lottie 애니메이션을 제작하고 관리하며 여러 플랫폼에 배포할 수 있는 도구와 런타임을 제공하는 motion design platform이다. LottieFiles는 기존 Lottie JSON과 이미지·폰트 같은 부가 리소스를 각각 관리해야 하는 문제를 줄이기 위해 dotLottie를 open-source format으로 공개했다. dotLottie는 Lottie를 대체하는 새로운 렌더링 규격이라기보다, Lottie animation과 실행에 필요한 데이터를 하나의 배포 단위로 묶는 패키지 규격에 가깝다.
.lottie는 ZIP 기반이며, animation뿐 아니라 이미지, 테마, 상태 머신 등의 데이터를 함께 포함할 수 있다. 구체적인 디렉터리 구성과 manifest 형식은 dotLottie specification에서 확인할 수 있다.
LottieFiles에서는 dotLottie를 다양한 환경에서 재생할 수 있도록 player를 제공하며, Rust로 작성된 dotlottie-rs를 공통 런타임으로 사용한다. 이 runtime은 ThorVG를 사용해서 화면을 그린다.
Lottie는 JSON으로 표현된 벡터 애니메이션 포맷이다. 기존 Lottie 생태계에서는 Web, Android, iOS 등 각 플랫폼이 별도의 player와 renderer를 구현하는 경우가 많았다. 이로 인해 같은 Lottie 파일이라도 지원 기능이나 렌더링 결과가 달라질 수 있고 새로운 기능을 여러 코드베이스에 반복해서 구현해야 하는 단점이 존재했다.
LottieFiles는 이를 해결하기 위해 공통 기능을 Rust 기반의 dotlottie-rs로 모았다. .lottie 해석, 재생 상태, theme, state machine 같은 핵심 로직을 한곳에 두고, Web에서는 wasm-bindgen으로 생성된 WASM 바인딩을, native platform에서는 C API를 사용해 같은 runtime을 호출한다.
dotlottie-rs가 Lottie scene parser와 vector rasterizer까지 새로 구현하는 것은 아니다. .lottie package 처리와 필요한 전처리는 Rust에서 수행하지만, 실제 Lottie animation scene의 해석과 vector rendering은 ThorVG에 위임한다.
dotlottie-web을 기준으로 정리해보면 아래와 같다.
영역
담당
.lottie 압축 해제와 manifest 해석
dotlottie-rs
animation·theme·state machine 선택
dotlottie-rs
play·pause·loop·speed·direction
dotlottie-rs
다음에 표시할 frame 계산
dotlottie-rs
Lottie animation scene 해석
ThorVG
해당 frame의 vector scene 계산
ThorVG
vector rasterization
ThorVG
Browser Canvas와 DOM event 연결
dotlottie-web
따라서 세 프로젝트의 관계는 다음처럼 정리할 수 있다.
dotlottie-web은 브라우저와 dotlottie-rs를 연결하고, dotlottie-rs는 무엇을 언제 재생할지 결정하며, ThorVG는 전달받은 Lottie animation과 frame을 해석해 실제 화면에 그린다.
2. 전체 프로젝트 구조
앞에서 나눈 역할을 Web의 실제 실행 계층으로 펼치면 dotlottie-web, dotlottie-rs, ThorVG의 세 단계로 정리할 수 있다. 브라우저에서 dotLottie animation이 출력되기까지의 전체 구조는 다음과 같다.
Application
│
▼
dotlottie-web
TypeScript API / HTML Canvas / Browser lifecycle
│
│ wasm-bindgen
▼
dotlottie-rs
Player / .lottie package / Theme / State Machine
│
│ ThorVG C API
▼
ThorVG
Lottie parsing / Scene evaluation / Rasterization
dotlottie-web
브라우저 개발자가 직접 사용하는 TypeScript API다.
대표적으로 다음 작업을 담당한다.
WASM module 초기화
URL 또는 JavaScript object에서 animation data 로드
HTML Canvas 연결
requestAnimationFrame loop 관리
Canvas 크기와 device pixel ratio 처리
pointer·click event 처리
Rust runtime event를 JavaScript event로 전달
Software·WebGL·WebGPU build 선택
dotlottie-rs
플랫폼에 독립적인 runtime core다.
주요 기능은 다음과 같다.
.lottie archive와 manifest 관리
animation 선택
current frame과 elapsed time 관리
play·pause·stop
loop·bounce·reverse
marker와 segment
theme와 slot
state machine
renderer 호출
ThorVG
실제 vector animation을 처리하는 graphics engine이다.
dotlottie-rs는 ThorVG의 다음 기능을 사용한다.
Lottie JSON load
animation frame 설정
animation 크기와 duration 조회
marker 조회
frame tweening
slot 적용
layer bounds 조회
Software·OpenGL·WebGPU Canvas
최종 rasterization
3. Submodule과 빌드
dotlottie-web은 dotlottie-rs를 일반 npm dependency가 아니라 저장소 내부의 Git submodule로 포함한다.
dotlottie-web의 .gitmodules를 보면 dotlottie-rs가 다음 경로에 연결되어 있다.
이 구조를 사용하면 각 상위 저장소가 의존 프로젝트의 특정 commit을 고정할 수 있다. 따라서 dotlottie-web은 검증된 dotlottie-rs revision을 사용하고, dotlottie-rs 역시 호환성이 확인된 ThorVG revision을 기준으로 빌드할 수 있다.
특히 ThorVG는 시스템에 설치된 library를 동적으로 찾는 대신, submodule로 포함된 source를 dotlottie-rs의 build 과정에서 target에 맞게 직접 컴파일한다. 덕분에 Software, WebGL, WebGPU 등 서로 다른 backend도 동일한 ThorVG revision을 기준으로 빌드할 수 있다.
전체 build 과정은 대략 다음과 같다.
Cargo feature로 backend와 필요한 기능 선택
↓
build.rs가 ThorVG source와 compile option 구성
↓
cc crate로 ThorVG C/C++ source 컴파일
↓
bindgen으로 ThorVG C API의 Rust FFI binding 생성
↓
Rust runtime과 함께 native library 또는 WASM으로 빌드
Cargo.toml의 feature를 통해 Software, GL, WebGPU backend와 필요한 loader 등을 선택하고, build.rs는 이에 맞춰 ThorVG source와 compile option을 구성한다.
이 과정에서 cc crate가 ThorVG C/C++ source를 컴파일하고, bindgen은 ThorVG의 C API header인 thorvg_capi.h를 기반으로 Rust FFI binding을 생성한다. 이렇게 생성된 ThorVG와 Rust runtime은 각 target에 맞는 native library 또는 WASM artifact로 빌드된다.
플랫폼별 build script는 이 Cargo build를 바탕으로 Web, Android, Apple, Linux, Windows용 artifact를 생성하며, Web에서는 Software, WebGL, WebGPU backend가 각각 별도의 WASM artifact로 빌드된다.
dotlottie-rs/
└── dotlottie-rs/
├── Cargo.toml # Rust 의존성과 ThorVG backend feature 설정
├── build.rs # ThorVG C/C++ source 빌드 및 Rust FFI binding 생성
├── deps/
│ └── thorvg/ # Git submodule로 포함된 ThorVG source
└── src/
├── player.rs # 재생 상태와 frame 진행 관리
├── layout.rs # animation과 canvas 간 layout 및 transform 계산
├── tween.rs # frame 간 tween 상태와 진행률 관리
├── state_machine/ # State Machine 실행과 interaction 상태 관리
├── wasm/ # dotlottie-rs API를 WebAssembly로 노출하는 binding
└── renderer/
├── backend.rs # Renderer / Animation / Shape 등의 backend trait 정의
├── mod.rs # LottieRenderer 추상화와 renderer orchestration
└── thorvg.rs # ThorVG C API 기반 Renderer / Animation 실제 구현
Renderer abstraction
Player가 ThorVG C API를 직접 호출하는 구조는 아니다. dotlottie-rs는 재생 로직과 실제 그래픽 엔진 호출 사이에 렌더러 추상화를 두고 있다.
flowchart TD
subgraph DotLottieRS["dotlottie-rs"]
Player["Player"]
LR["LottieRenderer"]
Renderer["Renderer trait<br/>(Canvas / Render Target)"]
Animation["Animation trait<br/>(Lottie / Frame State)"]
TR["TvgRenderer"]
TA["TvgAnimation"]
Player --> LR
LR --> Renderer
LR --> Animation
Renderer --> TR
Animation --> TA
end
subgraph ThorVG["ThorVG"]
Canvas["Canvas<br/>(SW / GL / WebGPU)"]
Anim["Animation / Picture"]
end
TR -->|"ThorVG C API"| Canvas
TA -->|"ThorVG C API"| Anim
Loading
이 구조에서 두 trait은 역할이 명확히 나뉜다.
Renderer는 선택된 backend에 맞는 Canvas와 render target을 설정하고, animation을 Canvas에 등록한 뒤 update, draw, sync와 같은 실제 렌더링 작업을 수행한다.
반면 Animation은 하나의 Lottie animation과 그 상태를 다루는 역할이다.
Lottie 데이터를 로드하고 현재 frame을 변경하거나, duration과 marker를 조회하고, tween·slot·transform과 같이 animation 내부 상태에 영향을 주는 기능을 제공한다.
따라서 두 interface는 다음과 같이 ThorVG 객체에 대응한다.
dotlottie-rs
ThorVG
역할
Renderer → TvgRenderer
Canvas
render target 설정 및 실제 drawing
Animation → TvgAnimation
Animation / Picture
Lottie load 및 animation 상태 제어
TvgRenderer와 TvgAnimation은 이 두 interface를 ThorVG C API로 구현한 어댑터다.
예를 들어 Rust에서 animation frame을 변경하면 TvgAnimation이 이를 ThorVG의 tvg_animation_set_frame() 호출로 변환하고, 렌더링 요청은 TvgRenderer를 거쳐 ThorVG Canvas의 update, draw, sync로 이어진다.
즉 Player는 재생 상태와 정책을 관리하고, renderer 계층은 그 결과를 ThorVG가 수행할 그래픽 명령으로 변환한다.
구조상 다른 renderer 구현을 추가할 여지는 있지만, 현재 interface의 기능은 frame, marker, tween, slot, Canvas update 등 ThorVG가 제공하는 기능과 밀접하게 대응한다. 따라서 완전히 렌더러 독립적인 계층이라기보다는, ThorVG를 Rust runtime에서 일관된 방식으로 사용하기 위한 abstraction에 가깝다.
5. .lottie 파일을 로드할 때의 실제 호출 흐름
브라우저에서 .lottie 파일을 불러오는 작업은 dotlottie-web의 DotLottie class에서 시작한다.
WASM binding을 통과한 데이터는 Rust의 Player::load_dotlottie_data()로 전달된다. 이 함수는 DotLottieManager를 생성해 .lottie archive와 manifest를 열고 active animation의 JSON을 가져온 뒤, LottieRenderer의 load_data()를 통해 renderer 계층으로 전달한다.
dotlottie-rs에서 ThorVG와 연결되는 어댑터 코드는 renderer/thorvg.rs에 구현되어 있다. renderer/mod.rs의 LottieRendererImpl::load_animation()은 Lottie 데이터를 "lottie+json" 형식으로 TvgAnimation::load_data()에 전달하고, TvgAnimation은 내부 tvg_load_data_dispatch()를 거쳐 최종적으로 ThorVG C API의 tvg_picture_load_data()를 호출한다.
bindgen은 C/C++ header를 분석해 Rust FFI binding을 자동 생성하는 도구로서, dotlottie-rs/build.rs에서 ThorVG의 C API header인 thorvg_capi.h를 토대로 bindings.rs를 생성하는 것을 볼 수 있다.
Animation이 재생되는 동안 dotlottie-web은 requestAnimationFrame마다 경과 시간 dt를 계산해 WASM core의 tick(dt)을 호출한다. dotlottie-rs는 이 dt와 speed, direction, loop, segment 등의 재생 설정을 바탕으로 현재 표시할 frame position을 결정하고, 이를 ThorVG에 전달한다.
먼저 advance_frames()에서는 animation의 FPS와 speed를 반영해 경과 시간을 frame 단위의 재생 위치로 변환한다.
이후 next_frame()은 이 재생 위치에 direction, loop, bounce, segment 등을 반영해 실제 frame position을 결정한다. Frame interpolation이 활성화된 경우에는 12.37과 같은 소수 frame을 사용할 수 있고, 비활성화된 경우에는 정수 frame으로 맞춘다.
fnapply_frame(&mutself,no:f32) -> Result<()>{if no < self.start_frame() || no > self.end_frame(){returnErr(Error::InvalidParameter);}self.renderer.set_frame(no)?;self.event_queue.push(PlayerEvent::Frame{frame_no: no });Ok(())}
ThorVG backend에서는 이 호출이 TvgAnimation::set_frame()으로 이어지고, 내부적으로 ThorVG C API의 tvg_animation_set_frame()을 호출해 현재 프레임을 설정한다.
Frame이 변경된 뒤 render가 요청되면 LottieRendererImpl은 ThorVG Canvas를 통해 rendering을 수행한다. 현재 구현에서는 이전 rendering 작업을 먼저 sync()한 뒤 update(), draw(), 다시 sync()하는 순서로 진행된다.
호출
역할
sync
이전에 요청된 rendering 작업의 완료를 보장
update
frame 변경 등으로 수정된 scene 상태를 rendering에 반영
draw
현재 scene을 설정된 render target에 그림
sync
요청한 rendering 작업이 완료될 때까지 동기화
7. ThorVG는 어디에 렌더링하고, 브라우저에는 어떻게 표시되는가?
앞 절에서는 dotlottie-rs가 현재 표시할 frame position을 결정하고, 이를 ThorVG에 전달하는 과정을 살펴봤다. 그렇다면 ThorVG는 전달받은 frame을 어디에 렌더링하고, 그 결과는 어떻게 브라우저의 Canvas까지 전달될까?
이는 사용하는 ThorVG Canvas backend에 따라 달라진다. 먼저 기본 @lottiefiles/dotlottie-web package가 사용하는 Software renderer를 살펴보자.
이 경우 상위의 Player, frame 계산, TvgAnimation 구조는 동일하지만 ThorVG Canvas에 연결되는 render target이 달라진다.
Backend
ThorVG Canvas
Render target
Browser 출력
Software
SwCanvas
WASM pixel buffer
ImageData → Canvas2D
WebGL
GlCanvas
WebGL context / target
WebGL
WebGPU
WgCanvas
WebGPU surface
WebGPU
Software backend에서는 CPU rasterization 결과를 WASM pixel buffer에 기록한 뒤 Canvas2D를 통해 표시한다. 반면 WebGL과 WebGPU backend에서는 ThorVG가 GPU render target에 직접 rendering하므로 Software 경로의 get_pixel_buffer() → ImageData → putImageData() 과정이 필요하지 않다.
8. 마무리
이렇게 dotLottie에 대해서 알아보았다.
ThorVG는 rendering뿐 아니라 marker, tween, slot 등의 animation 기능과 State Machine의 OBB 기반 hit testing처럼 Lottie scene의 정보를 활용해야 하는 기능에도 사용된다. 관심이 있다면 이 부분도 함께 살펴보면 재미있을 것이다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
dotLottie에서 ThorVG가 사용된 이야기
ThorVG가 실제 오픈소스 프로젝트에서 어떤 역할로 사용되는지 확인하기 위해 LottieFiles의
dotlottie-rs와dotlottie-web을 조사했다.LottieFiles/dotlottie-rsLottieFiles/dotlottie-webthorvg/thorvg두 LottieFiles 저장소를 처음 보면
dotlottie-web이 웹 플레이어이고dotlottie-rs는 별도의 Rust 구현처럼 보인다. 하지만 실제 구조는dotlottie-web이 WASM으로 빌드된dotlottie-rs를 사용하고, 다시dotlottie-rs가 ThorVG를 실제 렌더러로 사용하는 형태다.이 글에서는
.lottie파일이 브라우저에서 로드된 뒤 어떻게 ThorVG에 전달되고, 어떻게 화면에 출력되는지 알아볼 것이다.Note
Web 외에도 Android, iOS, Flutter, React Native용 dotLottie player가 존재한다. 이 글에서는 Web 환경을 기준으로 설명하지만, 구조와 역할은 다른 플랫폼에서도 유사하다.
0. dotLottie란 무엇인가?
dotLottie: An open-source animation package format for every platformLottieFiles는 Lottie 애니메이션을 제작하고 관리하며 여러 플랫폼에 배포할 수 있는 도구와 런타임을 제공하는 motion design platform이다. LottieFiles는 기존 Lottie JSON과 이미지·폰트 같은 부가 리소스를 각각 관리해야 하는 문제를 줄이기 위해 dotLottie를 open-source format으로 공개했다. dotLottie는 Lottie를 대체하는 새로운 렌더링 규격이라기보다, Lottie animation과 실행에 필요한 데이터를 하나의 배포 단위로 묶는 패키지 규격에 가깝다.
.lottie는 ZIP 기반이며, animation뿐 아니라 이미지, 테마, 상태 머신 등의 데이터를 함께 포함할 수 있다. 구체적인 디렉터리 구성과 manifest 형식은 dotLottie specification에서 확인할 수 있다.LottieFiles에서는 dotLottie를 다양한 환경에서 재생할 수 있도록 player를 제공하며, Rust로 작성된
dotlottie-rs를 공통 런타임으로 사용한다. 이 runtime은 ThorVG를 사용해서 화면을 그린다.Note
더 자세한 내용은 lottiefiles.com/dotlottie에서 확인할 수 있다.
1. dotLottie는 왜 별도의 Rust 런타임과 ThorVG를 사용하는가
Lottie는 JSON으로 표현된 벡터 애니메이션 포맷이다. 기존 Lottie 생태계에서는 Web, Android, iOS 등 각 플랫폼이 별도의 player와 renderer를 구현하는 경우가 많았다. 이로 인해 같은 Lottie 파일이라도 지원 기능이나 렌더링 결과가 달라질 수 있고 새로운 기능을 여러 코드베이스에 반복해서 구현해야 하는 단점이 존재했다.
LottieFiles는 이를 해결하기 위해 공통 기능을 Rust 기반의
dotlottie-rs로 모았다..lottie해석, 재생 상태, theme, state machine 같은 핵심 로직을 한곳에 두고, Web에서는wasm-bindgen으로 생성된 WASM 바인딩을, native platform에서는 C API를 사용해 같은 runtime을 호출한다.dotlottie-rs가 Lottie scene parser와 vector rasterizer까지 새로 구현하는 것은 아니다..lottiepackage 처리와 필요한 전처리는 Rust에서 수행하지만, 실제 Lottie animation scene의 해석과 vector rendering은 ThorVG에 위임한다.dotlottie-web을 기준으로 정리해보면 아래와 같다..lottie압축 해제와 manifest 해석dotlottie-rsdotlottie-rsdotlottie-rsdotlottie-rsdotlottie-web따라서 세 프로젝트의 관계는 다음처럼 정리할 수 있다.
2. 전체 프로젝트 구조
앞에서 나눈 역할을 Web의 실제 실행 계층으로 펼치면
dotlottie-web,dotlottie-rs, ThorVG의 세 단계로 정리할 수 있다. 브라우저에서 dotLottie animation이 출력되기까지의 전체 구조는 다음과 같다.dotlottie-web브라우저 개발자가 직접 사용하는 TypeScript API다.
대표적으로 다음 작업을 담당한다.
requestAnimationFrameloop 관리dotlottie-rs플랫폼에 독립적인 runtime core다.
주요 기능은 다음과 같다.
.lottiearchive와 manifest 관리ThorVG
실제 vector animation을 처리하는 graphics engine이다.
dotlottie-rs는 ThorVG의 다음 기능을 사용한다.3. Submodule과 빌드
dotlottie-web은dotlottie-rs를 일반 npm dependency가 아니라 저장소 내부의 Git submodule로 포함한다.dotlottie-web의.gitmodules를 보면dotlottie-rs가 다음 경로에 연결되어 있다.dotlottie-rs역시 ThorVG를 Git submodule로 포함하며,dotlottie-rs/.gitmodules에서 다음 경로를 확인할 수 있다.전체 저장소 관계는 다음과 같다.
이 구조를 사용하면 각 상위 저장소가 의존 프로젝트의 특정 commit을 고정할 수 있다. 따라서
dotlottie-web은 검증된dotlottie-rsrevision을 사용하고,dotlottie-rs역시 호환성이 확인된 ThorVG revision을 기준으로 빌드할 수 있다.특히 ThorVG는 시스템에 설치된 library를 동적으로 찾는 대신, submodule로 포함된 source를
dotlottie-rs의 build 과정에서 target에 맞게 직접 컴파일한다. 덕분에 Software, WebGL, WebGPU 등 서로 다른 backend도 동일한 ThorVG revision을 기준으로 빌드할 수 있다.전체 build 과정은 대략 다음과 같다.
Cargo.toml의 feature를 통해 Software, GL, WebGPU backend와 필요한 loader 등을 선택하고,build.rs는 이에 맞춰 ThorVG source와 compile option을 구성한다.이 과정에서
cccrate가 ThorVG C/C++ source를 컴파일하고,bindgen은 ThorVG의 C API header인thorvg_capi.h를 기반으로 Rust FFI binding을 생성한다. 이렇게 생성된 ThorVG와 Rust runtime은 각 target에 맞는 native library 또는 WASM artifact로 빌드된다.플랫폼별 build script는 이 Cargo build를 바탕으로 Web, Android, Apple, Linux, Windows용 artifact를 생성하며, Web에서는 Software, WebGL, WebGPU backend가 각각 별도의 WASM artifact로 빌드된다.
관련 build 구조는 다음 파일에서 확인할 수 있다.
Cargo.tomlbuild.rsBUILD_SYSTEM.md4.
dotlottie-rs내부에서 ThorVG는 어떻게 사용되는가dotlottie-rs의 폴더 구조 중 ThorVG와 관련된 부분은 다음과 같다.Renderer abstraction
Player가 ThorVG C API를 직접 호출하는 구조는 아니다.dotlottie-rs는 재생 로직과 실제 그래픽 엔진 호출 사이에 렌더러 추상화를 두고 있다.flowchart TD subgraph DotLottieRS["dotlottie-rs"] Player["Player"] LR["LottieRenderer"] Renderer["Renderer trait<br/>(Canvas / Render Target)"] Animation["Animation trait<br/>(Lottie / Frame State)"] TR["TvgRenderer"] TA["TvgAnimation"] Player --> LR LR --> Renderer LR --> Animation Renderer --> TR Animation --> TA end subgraph ThorVG["ThorVG"] Canvas["Canvas<br/>(SW / GL / WebGPU)"] Anim["Animation / Picture"] end TR -->|"ThorVG C API"| Canvas TA -->|"ThorVG C API"| Anim이 구조에서 두 trait은 역할이 명확히 나뉜다.
Renderer는 선택된 backend에 맞는 Canvas와 render target을 설정하고, animation을 Canvas에 등록한 뒤update,draw,sync와 같은 실제 렌더링 작업을 수행한다.반면
Animation은 하나의 Lottie animation과 그 상태를 다루는 역할이다.Lottie 데이터를 로드하고 현재 frame을 변경하거나, duration과 marker를 조회하고, tween·slot·transform과 같이 animation 내부 상태에 영향을 주는 기능을 제공한다.
따라서 두 interface는 다음과 같이 ThorVG 객체에 대응한다.
dotlottie-rsRenderer→TvgRendererCanvasAnimation→TvgAnimationAnimation/PictureTvgRenderer와TvgAnimation은 이 두 interface를 ThorVG C API로 구현한 어댑터다.예를 들어 Rust에서 animation frame을 변경하면
TvgAnimation이 이를 ThorVG의tvg_animation_set_frame()호출로 변환하고, 렌더링 요청은TvgRenderer를 거쳐 ThorVG Canvas의update,draw,sync로 이어진다.즉
Player는 재생 상태와 정책을 관리하고, renderer 계층은 그 결과를 ThorVG가 수행할 그래픽 명령으로 변환한다.구조상 다른 renderer 구현을 추가할 여지는 있지만, 현재 interface의 기능은 frame, marker, tween, slot, Canvas update 등 ThorVG가 제공하는 기능과 밀접하게 대응한다. 따라서 완전히 렌더러 독립적인 계층이라기보다는, ThorVG를 Rust runtime에서 일관된 방식으로 사용하기 위한 abstraction에 가깝다.
5.
.lottie파일을 로드할 때의 실제 호출 흐름브라우저에서
.lottie파일을 불러오는 작업은dotlottie-web의DotLottieclass에서 시작한다.src가 URL이면 TypeScript는fetch()를 사용해 데이터를 가져온다..lottie파일은ArrayBuffer형태로 읽은 뒤 WASM API에 전달된다.dotlottie-web/packages/web/src/dotlottie.ts의_loadFromData()를 보면ArrayBuffer를.lottie데이터인지 확인한 뒤 WASM core로 전달하는 코드를 확인할 수 있다.WASM binding을 통과한 데이터는 Rust의
Player::load_dotlottie_data()로 전달된다. 이 함수는DotLottieManager를 생성해.lottiearchive와 manifest를 열고 active animation의 JSON을 가져온 뒤,LottieRenderer의load_data()를 통해 renderer 계층으로 전달한다.dotlottie-rs에서 ThorVG와 연결되는 어댑터 코드는renderer/thorvg.rs에 구현되어 있다.renderer/mod.rs의LottieRendererImpl::load_animation()은 Lottie 데이터를"lottie+json"형식으로TvgAnimation::load_data()에 전달하고,TvgAnimation은 내부tvg_load_data_dispatch()를 거쳐 최종적으로 ThorVG C API의tvg_picture_load_data()를 호출한다.Note
bindgen은 C/C++ header를 분석해 Rust FFI binding을 자동 생성하는 도구로서,
dotlottie-rs/build.rs에서 ThorVG의 C API header인 thorvg_capi.h를 토대로 bindings.rs를 생성하는 것을 볼 수 있다.
전체 load 흐름을 다시 정리해보면 다음과 같다.
6. 재생 중 frame 계산과 ThorVG 렌더링
Animation이 재생되는 동안
dotlottie-web은requestAnimationFrame마다 경과 시간dt를 계산해 WASM core의tick(dt)을 호출한다.dotlottie-rs는 이dt와 speed, direction, loop, segment 등의 재생 설정을 바탕으로 현재 표시할 frame position을 결정하고, 이를 ThorVG에 전달한다.먼저
advance_frames()에서는 animation의 FPS와 speed를 반영해 경과 시간을 frame 단위의 재생 위치로 변환한다.이후
next_frame()은 이 재생 위치에 direction, loop, bounce, segment 등을 반영해 실제 frame position을 결정한다. Frame interpolation이 활성화된 경우에는12.37과 같은 소수 frame을 사용할 수 있고, 비활성화된 경우에는 정수 frame으로 맞춘다.결정된 frame은
Player::apply_frame()을 통해 renderer로 전달된다.ThorVG backend에서는 이 호출이
TvgAnimation::set_frame()으로 이어지고, 내부적으로 ThorVG C API의tvg_animation_set_frame()을 호출해 현재 프레임을 설정한다.전체 흐름을 정리하면 다음과 같다.
Frame이 변경된 뒤 render가 요청되면
LottieRendererImpl은 ThorVG Canvas를 통해 rendering을 수행한다. 현재 구현에서는 이전 rendering 작업을 먼저sync()한 뒤update(),draw(), 다시sync()하는 순서로 진행된다.syncupdatedrawsync7. ThorVG는 어디에 렌더링하고, 브라우저에는 어떻게 표시되는가?
앞 절에서는
dotlottie-rs가 현재 표시할 frame position을 결정하고, 이를 ThorVG에 전달하는 과정을 살펴봤다. 그렇다면 ThorVG는 전달받은 frame을 어디에 렌더링하고, 그 결과는 어떻게 브라우저의 Canvas까지 전달될까?이는 사용하는 ThorVG Canvas backend에 따라 달라진다. 먼저 기본
@lottiefiles/dotlottie-webpackage가 사용하는 Software renderer를 살펴보자.Software Canvas의 render target
Software backend에서는 HTML Canvas가 ThorVG의 직접적인 render target이 아니다.
대신 WASM wrapper인
DotLottiePlayerWasm이 다음과 같은 pixel buffer를 소유한다.이 buffer는 WASM linear memory에 위치하며, 한 개의
u32가 한 pixel을 나타낸다.Canvas의 크기가 결정되면
setup_sw_target()에서width × height크기의 buffer를 준비한다.이후
Player::set_sw_target()을 거쳐TvgRenderer::set_sw_target()에서tvg::tvg_swcanvas_set_targetFFI를 호출한다.여기서
frame_ptr.as_mut_ptr()를 통해 Rust의 pixel buffer 주소가 ThorVG에 전달된다.ThorVG C API의
tvg_swcanvas_set_target()도 전달받은 buffer pointer를 그대로SwCanvas::target()에 넘긴다.즉 Software backend에서 ThorVG의 최종 render target은
dotlottie-rs가 WASM memory에 생성한sw_buffer다.ThorVG가 별도의 최종 pixel buffer를 만들어 결과를 다시 Rust로 복사하는 것이 아니라, 미리 전달받은 buffer에 직접 rendering 결과를 기록하는 구조다.
WASM pixel buffer에서 HTML Canvas까지
ThorVG의 rendering이 완료되면 pixel image는 아직 WASM memory의
sw_buffer안에 있다.이를 브라우저에 표시하기 위해
DotLottiePlayerWasm은get_pixel_buffer()를 제공한다.여기서 새로운 pixel buffer를 생성하는 것이 아니라, WASM linear memory의
sw_buffer를 가리키는Uint8Arrayview를 만든다.dotlottie-web의dotlottie.ts에서는 이 buffer를 가져와ImageData로 만들고putImageData()를 사용해 브라우저 Canvas에 표시한다.전체 Software rendering 경로를 정리하면 다음과 같다.
WebGL과 WebGPU에서는?
dotlottie-web은 같은DotLottieAPI를 사용하는 WebGL과 WebGPU entry point도 제공한다.이 경우 상위의
Player, frame 계산,TvgAnimation구조는 동일하지만 ThorVG Canvas에 연결되는 render target이 달라진다.SwCanvasImageData→ Canvas2DGlCanvasWgCanvasSoftware backend에서는 CPU rasterization 결과를 WASM pixel buffer에 기록한 뒤 Canvas2D를 통해 표시한다. 반면 WebGL과 WebGPU backend에서는 ThorVG가 GPU render target에 직접 rendering하므로 Software 경로의
get_pixel_buffer() → ImageData → putImageData()과정이 필요하지 않다.8. 마무리
이렇게 dotLottie에 대해서 알아보았다.
ThorVG는 rendering뿐 아니라 marker, tween, slot 등의 animation 기능과 State Machine의 OBB 기반 hit testing처럼 Lottie scene의 정보를 활용해야 하는 기능에도 사용된다. 관심이 있다면 이 부분도 함께 살펴보면 재미있을 것이다.
All reactions