From fa57083552ac69dded61e42ecdcd037feec9399d Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 8 Nov 2020 17:01:44 +0000 Subject: [PATCH] LibGfx: gracefully handle GIFs with frame decode errors GIFLoader now tracks the state of errors during the decoding process and will fall back to displaying the first frame of the GIF if any of the subsequent frames fail to decode. --- Libraries/LibGfx/GIFLoader.cpp | 49 ++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/Libraries/LibGfx/GIFLoader.cpp b/Libraries/LibGfx/GIFLoader.cpp index 06505238a4..d136e81f73 100644 --- a/Libraries/LibGfx/GIFLoader.cpp +++ b/Libraries/LibGfx/GIFLoader.cpp @@ -81,11 +81,17 @@ struct LogicalScreen { struct GIFLoadingContext { enum State { NotDecoded = 0, - Error, FrameDescriptorsLoaded, FrameComplete, }; State state { NotDecoded }; + enum ErrorState { + NoError = 0, + FailedToDecodeAllFrames, + FailedToDecodeAnyFrame, + FailedToLoadFrameDescriptors, + }; + ErrorState error_state { NoError }; const u8* data { nullptr }; size_t data_size { 0 }; LogicalScreen logical_screen {}; @@ -374,10 +380,10 @@ static bool decode_frame(GIFLoadingContext& context, size_t frame_index) } } } - } - context.current_frame = frame_index; - context.state = GIFLoadingContext::State::FrameComplete; + context.current_frame = i; + context.state = GIFLoadingContext::State::FrameComplete; + } return true; } @@ -624,13 +630,13 @@ GIFImageDecoderPlugin::~GIFImageDecoderPlugin() { } IntSize GIFImageDecoderPlugin::size() { - if (m_context->state == GIFLoadingContext::State::Error) { + if (m_context->error_state == GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors) { return {}; } if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (!load_gif_frame_descriptors(*m_context)) { - m_context->state = GIFLoadingContext::State::Error; + m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; return {}; } } @@ -669,9 +675,13 @@ bool GIFImageDecoderPlugin::sniff() bool GIFImageDecoderPlugin::is_animated() { + if (m_context->error_state != GIFLoadingContext::ErrorState::NoError) { + return false; + } + if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (!load_gif_frame_descriptors(*m_context)) { - m_context->state = GIFLoadingContext::State::Error; + m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; return false; } } @@ -681,9 +691,13 @@ bool GIFImageDecoderPlugin::is_animated() size_t GIFImageDecoderPlugin::loop_count() { + if (m_context->error_state != GIFLoadingContext::ErrorState::NoError) { + return 0; + } + if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (!load_gif_frame_descriptors(*m_context)) { - m_context->state = GIFLoadingContext::State::Error; + m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; return 0; } } @@ -693,9 +707,13 @@ size_t GIFImageDecoderPlugin::loop_count() size_t GIFImageDecoderPlugin::frame_count() { + if (m_context->error_state != GIFLoadingContext::ErrorState::NoError) { + return 1; + } + if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (!load_gif_frame_descriptors(*m_context)) { - m_context->state = GIFLoadingContext::State::Error; + m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; return 1; } } @@ -705,20 +723,23 @@ size_t GIFImageDecoderPlugin::frame_count() ImageFrameDescriptor GIFImageDecoderPlugin::frame(size_t i) { - if (m_context->state == GIFLoadingContext::State::Error) { + if (m_context->error_state >= GIFLoadingContext::ErrorState::FailedToDecodeAnyFrame) { return {}; } if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (!load_gif_frame_descriptors(*m_context)) { - m_context->state = GIFLoadingContext::State::Error; + m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; return {}; } } - if (!decode_frame(*m_context, i)) { - m_context->state = GIFLoadingContext::State::Error; - return {}; + if (m_context->error_state == GIFLoadingContext::ErrorState::NoError && !decode_frame(*m_context, i)) { + if (m_context->state < GIFLoadingContext::State::FrameComplete || !decode_frame(*m_context, 0)) { + m_context->error_state = GIFLoadingContext::ErrorState::FailedToDecodeAnyFrame; + return {}; + } + m_context->error_state = GIFLoadingContext::ErrorState::FailedToDecodeAllFrames; } ImageFrameDescriptor frame {};