Serenity Operating System
1/*
2 * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <LibCore/MappedFile.h>
9#include <LibCore/MimeData.h>
10#include <LibGUI/ImageWidget.h>
11#include <LibGUI/Painter.h>
12#include <LibGfx/Bitmap.h>
13#include <LibGfx/ImageDecoder.h>
14
15REGISTER_WIDGET(GUI, ImageWidget)
16
17namespace GUI {
18
19ImageWidget::ImageWidget(StringView)
20 : m_timer(Core::Timer::try_create().release_value_but_fixme_should_propagate_errors())
21
22{
23 set_frame_thickness(0);
24 set_frame_shadow(Gfx::FrameShadow::Plain);
25 set_frame_shape(Gfx::FrameShape::NoFrame);
26 set_auto_resize(true);
27
28 REGISTER_BOOL_PROPERTY("auto_resize", auto_resize, set_auto_resize);
29 REGISTER_BOOL_PROPERTY("should_stretch", should_stretch, set_should_stretch);
30 REGISTER_WRITE_ONLY_STRING_PROPERTY("bitmap", load_from_file);
31}
32
33void ImageWidget::set_bitmap(Gfx::Bitmap const* bitmap)
34{
35 if (m_bitmap == bitmap)
36 return;
37
38 m_bitmap = bitmap;
39 if (m_bitmap && m_auto_resize)
40 set_fixed_size(m_bitmap->size());
41
42 update();
43}
44
45void ImageWidget::set_auto_resize(bool value)
46{
47 m_auto_resize = value;
48
49 if (m_bitmap)
50 set_fixed_size(m_bitmap->size());
51}
52
53// Same as ImageViewer::ViewWidget::animate(), you probably want to keep any changes in sync
54void ImageWidget::animate()
55{
56 m_current_frame_index = (m_current_frame_index + 1) % m_image_decoder->frame_count();
57
58 auto current_frame = m_image_decoder->frame(m_current_frame_index).release_value_but_fixme_should_propagate_errors();
59 set_bitmap(current_frame.image);
60
61 if (current_frame.duration != m_timer->interval()) {
62 m_timer->restart(current_frame.duration);
63 }
64
65 if (m_current_frame_index == m_image_decoder->frame_count() - 1) {
66 ++m_loops_completed;
67 if (m_loops_completed > 0 && m_loops_completed == m_image_decoder->loop_count()) {
68 m_timer->stop();
69 }
70 }
71}
72
73void ImageWidget::load_from_file(StringView path)
74{
75 auto file_or_error = Core::MappedFile::map(path);
76 if (file_or_error.is_error())
77 return;
78
79 auto& mapped_file = *file_or_error.value();
80 auto mime_type = Core::guess_mime_type_based_on_filename(path);
81 m_image_decoder = Gfx::ImageDecoder::try_create_for_raw_bytes(mapped_file.bytes(), mime_type);
82 VERIFY(m_image_decoder);
83
84 auto frame = m_image_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
85 auto bitmap = frame.image;
86 VERIFY(bitmap);
87
88 set_bitmap(bitmap);
89
90 if (m_image_decoder->is_animated() && m_image_decoder->frame_count() > 1) {
91 auto first_frame = m_image_decoder->frame(0).release_value_but_fixme_should_propagate_errors();
92 m_timer->set_interval(first_frame.duration);
93 m_timer->on_timeout = [this] { animate(); };
94 m_timer->start();
95 }
96}
97
98void ImageWidget::mousedown_event(GUI::MouseEvent&)
99{
100 if (on_click)
101 on_click();
102}
103
104void ImageWidget::paint_event(PaintEvent& event)
105{
106 Frame::paint_event(event);
107
108 if (!m_bitmap) {
109 return;
110 }
111
112 Painter painter(*this);
113 painter.add_clip_rect(event.rect());
114
115 if (m_should_stretch) {
116 painter.draw_scaled_bitmap(frame_inner_rect(), *m_bitmap, m_bitmap->rect(), (float)opacity_percent() / 100.0f);
117 } else {
118 auto location = frame_inner_rect().center().translated(-(m_bitmap->width() / 2), -(m_bitmap->height() / 2));
119 painter.blit(location, *m_bitmap, m_bitmap->rect(), (float)opacity_percent() / 100.0f);
120 }
121}
122
123void ImageWidget::set_opacity_percent(int percent)
124{
125 if (m_opacity_percent == percent)
126 return;
127 m_opacity_percent = percent;
128 update();
129}
130
131}