GIF saver는 Animation을 프레임별로 소프트웨어 렌더링한 뒤 하나의 애니메이션 GIF 파일로 인코딩한다. 일반 Paint 저장은 지원하지 않는다.
전체 처리 흐름
Saver::save(Animation*, "*.gif")은 확장자로 GifSaver를 선택하고 저장 작업을 TaskScheduler에 요청한다.
- 애니메이션의 바운드로 출력 크기를 정하고 ABGR8888 버퍼를 할당한다.
SwCanvas에 배경과 애니메이션의 Picture를 추가한다.- FPS 간격으로 프레임을 이동하며 Canvas에 렌더링한다.
- FPS를 지정하지 않으면
totalFrame / duration으로 계산한다. - 최대 FPS 60
- 프레임 지연 시간을 1/100초 단위 정수로 기록하므로 실제 재생 속도와 요청한 FPS가 정확히 일치하지 않을 수 있다.
- FPS를 지정하지 않으면
- 렌더링한 픽셀을 GIF encoder에 전달한다.
- 각 프레임을 최대 256색의 로컬 팔레트로 변환하고 LZW 방식으로 압축한다.
- 팔레트 인덱스 0은 투명색으로 예약하며 디더링은 적용하지 않는다.
- 배경이 없으면 알파 값이 127 미만인 픽셀을 투명하게 처리한다.
- 배경이 있으면 이전 프레임과 같은 픽셀을 투명 인덱스로 기록한다.
- GIF를 무한 반복으로 설정하고 파일 기록을 마친다.
Saver::sync()에서 작업 완료를 기다리고 자원을 정리한다.
프레임 렌더링과 encoder 전달은 GifSaver::run()의 다음 루프에서 수행한다.
for (auto p = 0.0f; p < duration; p += delay) {
auto frameNo = animation->totalFrame() * (p / duration);
animation->frame(frameNo);
canvas->update();
if (canvas->draw(true) == tvg::Result::Success) {
canvas->sync();
}
if (!gifWriteFrame(&writer, reinterpret_cast<uint8_t*>(buffer), w, h, uint32_t(delay * 100.0f), transparent)) {
TVGERR("GIF_SAVER", "Failed gif encoding");
break;
}
}
GIF saver는 Animation만 지원하며 일반 Paint 저장, 반복 횟수 설정과 대문자 .GIF 확장자를 지원하지 않는다.
quality 인자 사용하지 않는다.
PNG saver를 구현한다면?
문제
core에는 PNG saver가 없어 Saver::save(Paint*, ...)로 정적 이미지를 저장할 수 없다.
현재 loader용 LodePNG는 디코딩 기능만 포함하므로 PNG 인코딩 경로도 없다.
해결 방향
LodePNG를 공용 PNG codec으로 구성해 loader와 saver가 함께 사용한다.
common에 PNG codec을 두고 loader용 디코드와 saver용 인코드를 빌드 옵션으로 구분한다.PngSaver를SaveModule과Task기반으로 구현한다.Paint의 바운드로 출력 크기를 정하고 빈 영역을 검사한다.- 배경과
Paint를SwCanvas에 한 번 렌더링한다. - RGBA 버퍼를 PNG codec으로 인코딩한다.
- Meson의 saver 옵션과
Saver의.png분기를 추가한다. - 첫 구현은 8비트 RGBA와 알파 보존만 지원하며
quality는 사용하지 않는다.
#3874 에서 구현되었다.
댓글
Discussion 원문