0927
20250927
카테고리: 게임엔진, GIF 렌더링

GIF 렌더링을 구현했다.
원래는 stb를 써서 했었는데,
stb가 완전하지 않다는 걸 알게 된 후 -
giflib 이라는 라이브러리를 가져와서 로딩하게 되었다.
stb로 gif를 로딩했을 때
내가 겪은 문제는 3개가 있다.
- disposal mode 기능을 지원하질 않아서, 일부 gif에 잔상(이전프레임) 이 남는다.
- 여백이어야 할 부분에 이상한 색이 채워져 있거나, 특정 부분이 너무 밝게 나온다(이건 압축 관련 문제일듯).
- duration 을 제대로 못 읽는다. 아예 읽지 못하거나, 지연시간을 너무 짧게 읽는다.
RenderDoc 으로 캡처해서 (1) 과 (2) 를 시각화해 보았다.
밑에 그림을 참고하길 바란다.

왼쪽이 giflib, 오른쪽이 stb_image이다.
첫 번째 행은 disposal mode 오류,
두 번째 행은 아티팩트 오류이다.
아무튼 렌더링 과정은 다음과 같다.
giftex_arr = renderer->create_texture_2d_array();
giftex_arr->initialize_empty(seq.get_unit_size(), seq.get_image_count(), dp::bcknd::TextureDesc::make_for_rgba());
for (uint32_t i = 0; i < seq.get_image_count(); i++) {
giftex_arr->upload_sub_image(
{ 0, 0, i },
seq.get_unit_size(),
1,
dp::bcknd::PixelFormat::RGBA,
dp::bcknd::DataType::UnsignedByte,
0,
seq.get_image_data_at(i)
);
}
for (int i = 0; i < seq.get_image_count(); i++) {
giftex_durs.push_back(seq.get_delay_data_at(i));
}
아주아주 옛날에는
Texture2D 를 여러개 만들었었는데,
이번에는 Texture2DArray 를 써보자 해서 그걸로 구현해놨다.
중요한 게 있다면
(처음에는 자세히 몰랐지만)
GIF 표준에 '프레임과 프레임 사이의 duration' 속성이 존재한다는 것이다.
update() 에서 해당 프레임 이후의 duration 만큼 쉬어주는 것도
구현을 해 주었다.
마지막에는 AnimatedSprite2D 라는 클래스로
특수화해주었다.
class AnimatedSprite2D : public IObject2D {
DP_OBJECT_DEF(AnimatedSprite2D, IObject2D);
private:
const bcknd::ITexture2DArray* m_texture_array = nullptr;
private:
uint32_t m_frame = 0;
uint32_t m_max_frame_count = 0;
private:
std::vector<double> m_durations{};
size_t m_duration_index = 0;
double m_last_time = 0.0;
private:
bool m_is_playing = false;
bool m_animation_repeated = true /*false*/;
private:
Vector2f m_size = { 64, 64 };
Color4f m_modulate = White;
public:
AnimatedSprite2D(IScene& owner, const std::string& name);
virtual ~AnimatedSprite2D() override;