diff options
author | Linus Groh <mail@linusgroh.de> | 2021-03-16 23:37:44 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-03-17 12:37:48 +0100 |
commit | 10843a2c8cec6cd3bbb0b14a84fe1a19eee89808 (patch) | |
tree | d13daac7a8ec6d254792384c6be2bf1379482776 | |
parent | 41e51554679dcc72c235ac44f466c1d7f738fc40 (diff) | |
download | serenity-10843a2c8cec6cd3bbb0b14a84fe1a19eee89808.zip |
QuickShow: Animate animated images :^)
With a little help (read: copy & paste) from ImageWidget, QuickShow will
now cycle through the frames of animated images - enjoy the cat GIFs!
Future improvement: cache decoded images like LibWeb's ImageResource to
waste less CPU - the same applies to LibGUI though, maybe we can put
something shared in LibGfx.
Closes #5837.
-rw-r--r-- | Userland/Applications/QuickShow/QSWidget.cpp | 56 | ||||
-rw-r--r-- | Userland/Applications/QuickShow/QSWidget.h | 18 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/ImageWidget.cpp | 1 |
3 files changed, 67 insertions, 8 deletions
diff --git a/Userland/Applications/QuickShow/QSWidget.cpp b/Userland/Applications/QuickShow/QSWidget.cpp index 6cc2c125ab..7a52e43fe6 100644 --- a/Userland/Applications/QuickShow/QSWidget.cpp +++ b/Userland/Applications/QuickShow/QSWidget.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Linus Groh <mail@linusgroh.de> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,16 +26,18 @@ */ #include "QSWidget.h" +#include <AK/MappedFile.h> #include <AK/StringBuilder.h> #include <LibCore/DirIterator.h> +#include <LibCore/Timer.h> #include <LibGUI/MessageBox.h> #include <LibGUI/Painter.h> -#include <LibGUI/Window.h> #include <LibGfx/Bitmap.h> #include <LibGfx/Orientation.h> #include <LibGfx/Palette.h> QSWidget::QSWidget() + : m_timer(Core::Timer::construct()) { set_fill_with_background_color(false); } @@ -244,12 +247,31 @@ void QSWidget::mousewheel_event(GUI::MouseEvent& event) void QSWidget::load_from_file(const String& path) { - auto bitmap = Gfx::Bitmap::load_from_file(path); - if (!bitmap) { + auto show_error = [&] { GUI::MessageBox::show(window(), String::formatted("Failed to open {}", path), "Cannot open image", GUI::MessageBox::Type::Error); + }; + + auto file_or_error = MappedFile::map(path); + if (file_or_error.is_error()) { + show_error(); + return; + } + + auto& mapped_file = *file_or_error.value(); + m_image_decoder = Gfx::ImageDecoder::create((const u8*)mapped_file.data(), mapped_file.size()); + auto bitmap = m_image_decoder->bitmap(); + if (!bitmap) { + show_error(); return; } + if (m_image_decoder->is_animated() && m_image_decoder->frame_count() > 1) { + const auto& first_frame = m_image_decoder->frame(0); + m_timer->set_interval(first_frame.duration); + m_timer->on_timeout = [this] { animate(); }; + m_timer->start(); + } + m_path = path; m_bitmap = bitmap; m_scale = -1; @@ -287,3 +309,31 @@ void QSWidget::reset_view() m_pan_origin = { 0, 0 }; set_scale(100); } + +void QSWidget::set_bitmap(const Gfx::Bitmap* bitmap) +{ + if (m_bitmap == bitmap) + return; + m_bitmap = bitmap; + update(); +} + +// Same as ImageWidget::animate(), you probably want to keep any changes in sync +void QSWidget::animate() +{ + m_current_frame_index = (m_current_frame_index + 1) % m_image_decoder->frame_count(); + + const auto& current_frame = m_image_decoder->frame(m_current_frame_index); + set_bitmap(current_frame.image); + + if (current_frame.duration != m_timer->interval()) { + m_timer->restart(current_frame.duration); + } + + if (m_current_frame_index == m_image_decoder->frame_count() - 1) { + ++m_loops_completed; + if (m_loops_completed > 0 && m_loops_completed == m_image_decoder->loop_count()) { + m_timer->stop(); + } + } +} diff --git a/Userland/Applications/QuickShow/QSWidget.h b/Userland/Applications/QuickShow/QSWidget.h index 57bc346f5b..c39ceb9f4b 100644 --- a/Userland/Applications/QuickShow/QSWidget.h +++ b/Userland/Applications/QuickShow/QSWidget.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Linus Groh <mail@linusgroh.de> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,12 +27,12 @@ #pragma once +#include <LibCore/Timer.h> #include <LibGUI/Frame.h> #include <LibGfx/Bitmap.h> +#include <LibGfx/ImageDecoder.h> #include <LibGfx/Point.h> -class QSLabel; - class QSWidget final : public GUI::Frame { C_OBJECT(QSWidget) public: @@ -72,18 +73,25 @@ private: virtual void mousewheel_event(GUI::MouseEvent&) override; virtual void drop_event(GUI::DropEvent&) override; + void set_bitmap(const Gfx::Bitmap* bitmap); + void relayout(); void resize_window(); void reset_view(); + void animate(); String m_path; RefPtr<Gfx::Bitmap> m_bitmap; - int m_toolbar_height { 28 }; - Gfx::IntRect m_bitmap_rect; + + RefPtr<Gfx::ImageDecoder> m_image_decoder; + size_t m_current_frame_index { 0 }; + size_t m_loops_completed { 0 }; + NonnullRefPtr<Core::Timer> m_timer; + int m_scale { -1 }; + int m_toolbar_height { 28 }; Gfx::FloatPoint m_pan_origin; - Gfx::IntPoint m_click_position; Gfx::FloatPoint m_saved_pan_origin; Vector<String> m_files_in_same_dir; diff --git a/Userland/Libraries/LibGUI/ImageWidget.cpp b/Userland/Libraries/LibGUI/ImageWidget.cpp index 218c09a809..24f68398e7 100644 --- a/Userland/Libraries/LibGUI/ImageWidget.cpp +++ b/Userland/Libraries/LibGUI/ImageWidget.cpp @@ -71,6 +71,7 @@ void ImageWidget::set_auto_resize(bool value) set_fixed_size(m_bitmap->size()); } +// Same as QSWidget::animate(), you probably want to keep any changes in sync void ImageWidget::animate() { m_current_frame_index = (m_current_frame_index + 1) % m_image_decoder->frame_count(); |