diff options
author | Peter Nelson <peter@peterdn.com> | 2020-08-31 13:22:38 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-08-31 18:54:44 +0200 |
commit | 259f8541fcd6bc147c9fb4c57b16cd840700af59 (patch) | |
tree | e7c54e7dd30679cab14d1fbe235eeb9518fa912f | |
parent | c3ee5e34514574878e36aaf036fac5554e9fd367 (diff) | |
download | serenity-259f8541fcd6bc147c9fb4c57b16cd840700af59.zip |
LibGfx: implement GIF RestorePrevious frame disposal mode
-rw-r--r-- | Base/res/html/misc/gifsuite.html | 9 | ||||
-rw-r--r-- | Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-0.png | bin | 0 -> 479 bytes | |||
-rw-r--r-- | Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-1.png | bin | 0 -> 315 bytes | |||
-rw-r--r-- | Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-2.png | bin | 0 -> 316 bytes | |||
-rw-r--r-- | Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-3.png | bin | 0 -> 315 bytes | |||
-rw-r--r-- | Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop.gif | bin | 0 -> 536 bytes | |||
-rw-r--r-- | Libraries/LibGfx/GIFLoader.cpp | 54 |
7 files changed, 43 insertions, 20 deletions
diff --git a/Base/res/html/misc/gifsuite.html b/Base/res/html/misc/gifsuite.html index 4122c22be0..a1c1766392 100644 --- a/Base/res/html/misc/gifsuite.html +++ b/Base/res/html/misc/gifsuite.html @@ -105,6 +105,15 @@ </tr> <tr> + <td class="b"><img src="gifsuite_files/animated_transparent_firstframerestoreprev_loop.gif"></td> + <td class="b"><img src="gifsuite_files/animated_transparent_firstframerestoreprev_loop-0.png"></td> + <td class="b"><img src="gifsuite_files/animated_transparent_firstframerestoreprev_loop-1.png"></td> + <td class="b"><img src="gifsuite_files/animated_transparent_firstframerestoreprev_loop-2.png"></td> + <td class="b"><img src="gifsuite_files/animated_transparent_firstframerestoreprev_loop-3.png"></td> + <td>Transparent gif with 4 frames, loops forever, first frame restore previous</td> + </tr> + + <tr> <td class="b"><img src="gifsuite_files/animated_transparent_frame_norestore_loop.gif"></td> <td class="b"><img src="gifsuite_files/animated_transparent_frame_norestore_loop-0.png"></td> <td class="b"><img src="gifsuite_files/animated_transparent_frame_norestore_loop-1.png"></td> diff --git a/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-0.png b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-0.png Binary files differnew file mode 100644 index 0000000000..302a26a4a3 --- /dev/null +++ b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-0.png diff --git a/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-1.png b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-1.png Binary files differnew file mode 100644 index 0000000000..191d9d176c --- /dev/null +++ b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-1.png diff --git a/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-2.png b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-2.png Binary files differnew file mode 100644 index 0000000000..545c1c087a --- /dev/null +++ b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-2.png diff --git a/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-3.png b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-3.png Binary files differnew file mode 100644 index 0000000000..584aacf282 --- /dev/null +++ b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop-3.png diff --git a/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop.gif b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop.gif Binary files differnew file mode 100644 index 0000000000..cbec149012 --- /dev/null +++ b/Base/res/html/misc/gifsuite_files/animated_transparent_firstframerestoreprev_loop.gif diff --git a/Libraries/LibGfx/GIFLoader.cpp b/Libraries/LibGfx/GIFLoader.cpp index 12f0d376a1..b8897a8f9d 100644 --- a/Libraries/LibGfx/GIFLoader.cpp +++ b/Libraries/LibGfx/GIFLoader.cpp @@ -93,6 +93,7 @@ struct GIFLoadingContext { size_t loops { 1 }; RefPtr<Gfx::Bitmap> frame_buffer; size_t current_frame { 0 }; + RefPtr<Gfx::Bitmap> prev_frame_buffer; }; RefPtr<Gfx::Bitmap> load_gif(const StringView& path) @@ -266,6 +267,12 @@ private: Vector<u8> m_output {}; }; +static void copy_frame_buffer(Bitmap& dest, const Bitmap& src) +{ + ASSERT(dest.size_in_bytes() == src.size_in_bytes()); + memcpy(dest.scanline(0), src.scanline(0), dest.size_in_bytes()); +} + static bool decode_frame(GIFLoadingContext& context, size_t frame_index) { if (frame_index >= context.images.size()) { @@ -280,6 +287,7 @@ static bool decode_frame(GIFLoadingContext& context, size_t frame_index) if (context.state < GIFLoadingContext::State::FrameComplete) { start_frame = 0; context.frame_buffer = Bitmap::create_purgeable(BitmapFormat::RGBA32, { context.logical_screen.width, context.logical_screen.height }); + context.prev_frame_buffer = Bitmap::create_purgeable(BitmapFormat::RGBA32, { context.logical_screen.width, context.logical_screen.height }); } else if (frame_index < context.current_frame) { start_frame = 0; } @@ -288,30 +296,40 @@ static bool decode_frame(GIFLoadingContext& context, size_t frame_index) auto& image = context.images.at(i); printf("Image %zu: %d,%d %dx%d %zu bytes LZW-encoded\n", i, image.x, image.y, image.width, image.height, image.lzw_encoded_bytes.size()); - LZWDecoder decoder(image.lzw_encoded_bytes, image.lzw_min_code_size); - - // Add GIF-specific control codes - const int clear_code = decoder.add_control_code(); - const int end_of_information_code = decoder.add_control_code(); - - auto& color_map = image.use_global_color_map ? context.logical_screen.color_map : image.color_map; - - auto background_color = color_map[context.background_color_index]; + const auto previous_image_disposal_method = i > 0 ? context.images.at(i - 1).disposal_method : ImageDescriptor::DisposalMethod::None; if (i == 0) { - context.frame_buffer->fill(background_color); + context.frame_buffer->fill(Color::Transparent); + } else if (i > 0 && image.disposal_method == ImageDescriptor::DisposalMethod::RestorePrevious + && previous_image_disposal_method != ImageDescriptor::DisposalMethod::RestorePrevious) { + // This marks the start of a run of frames that once disposed should be restored to the + // previous underlying image contents. Therefore we make a copy of the current frame + // buffer so that it can be restored later. + copy_frame_buffer(*context.prev_frame_buffer, *context.frame_buffer); } - const auto previous_image_disposal_method = i > 0 ? context.images.at(i - 1).disposal_method : ImageDescriptor::DisposalMethod::None; - if (previous_image_disposal_method == ImageDescriptor::DisposalMethod::RestoreBackground) { + // Note: RestoreBackground could be interpreted either as restoring the underlying + // background of the entire image (e.g. container element's background-color), or the + // background color of the GIF itself. It appears that all major browsers and most other + // GIF decoders adhere to the former interpretation, therefore we will do the same by + // clearing the entire frame buffer to transparent. Painter painter(*context.frame_buffer); painter.clear_rect(context.images.at(i - 1).rect(), Color::Transparent); - } else if (i > 1 && previous_image_disposal_method == ImageDescriptor::DisposalMethod::RestorePrevious) { - // TODO: tricky as it potentially requires remembering _all_ previous frames. - // Luckily it seems GIFs with this mode are rare in the wild. + } else if (i > 0 && previous_image_disposal_method == ImageDescriptor::DisposalMethod::RestorePrevious) { + // Previous frame indicated that once disposed, it should be restored to *its* previous + // underlying image contents, therefore we restore the saved previous frame buffer. + copy_frame_buffer(*context.frame_buffer, *context.prev_frame_buffer); } + LZWDecoder decoder(image.lzw_encoded_bytes, image.lzw_min_code_size); + + // Add GIF-specific control codes + const int clear_code = decoder.add_control_code(); + const int end_of_information_code = decoder.add_control_code(); + + const auto& color_map = image.use_global_color_map ? context.logical_screen.color_map : image.color_map; + int pixel_index = 0; int row = 0; int interlace_pass = 0; @@ -337,11 +355,7 @@ static bool decode_frame(GIFLoadingContext& context, size_t frame_index) int x = pixel_index % image.width + image.x; int y = row + image.y; - if (image.transparent && color == image.transparency_index) { - c.set_alpha(0); - } - - if (!image.transparent || previous_image_disposal_method == ImageDescriptor::DisposalMethod::None || color != image.transparency_index) { + if (!image.transparent || color != image.transparency_index) { context.frame_buffer->set_pixel(x, y, c); } |