summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2021-05-10 14:00:06 -0700
committerAndreas Kling <kling@serenityos.org>2021-05-18 16:35:23 +0200
commitc3c2121b5730f6b7307a93003b04281cd980e601 (patch)
tree6a532e0a10fa4d45d77e55eacc5c5db17e231cdf /Userland
parentd5f94aaa7b84d313613808f813c97354e504c7ba (diff)
downloadserenity-c3c2121b5730f6b7307a93003b04281cd980e601.zip
PDFViewer: Allow zooming in and out + scrolling
When holding ctrl and scrolling, the page will be zoomed in an out. When zoomed in on a page, scrolling with move vertically up and down the page (or horizontally if shift is being held). In order to speed up zooming, zoomed bitmaps are cached per-page at each distinct zoom level. This cache is cleared every 30 seconds to prevent OOM problems.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Applications/PDFViewer/PDFViewer.cpp92
-rw-r--r--Userland/Applications/PDFViewer/PDFViewer.h34
2 files changed, 104 insertions, 22 deletions
diff --git a/Userland/Applications/PDFViewer/PDFViewer.cpp b/Userland/Applications/PDFViewer/PDFViewer.cpp
index a69fcb4bfa..d2e018cb50 100644
--- a/Userland/Applications/PDFViewer/PDFViewer.cpp
+++ b/Userland/Applications/PDFViewer/PDFViewer.cpp
@@ -14,27 +14,33 @@ PDFViewer::PDFViewer()
set_should_hide_unnecessary_scrollbars(true);
set_focus_policy(GUI::FocusPolicy::StrongFocus);
set_scrollbars_enabled(true);
-}
-PDFViewer::~PDFViewer()
-{
+ start_timer(30'000);
}
void PDFViewer::set_document(RefPtr<PDF::Document> document)
{
m_document = document;
m_current_page_index = document->get_first_page_index();
+ m_zoom_level = initial_zoom_level;
+ m_rendered_page_list.clear();
+
+ m_rendered_page_list.ensure_capacity(document->get_page_count());
+ for (u32 i = 0; i < document->get_page_count(); i++)
+ m_rendered_page_list.unchecked_append(HashMap<u32, RefPtr<Gfx::Bitmap>>());
+
update();
}
RefPtr<Gfx::Bitmap> PDFViewer::get_rendered_page(u32 index)
{
- auto existing_rendered_page = m_rendered_pages.get(index);
+ auto& rendered_page_map = m_rendered_page_list[index];
+ auto existing_rendered_page = rendered_page_map.get(m_zoom_level);
if (existing_rendered_page.has_value())
return existing_rendered_page.value();
auto rendered_page = render_page(m_document->get_page(index));
- m_rendered_pages.set(index, rendered_page);
+ rendered_page_map.set(m_zoom_level, rendered_page);
return rendered_page;
}
@@ -50,39 +56,85 @@ void PDFViewer::paint_event(GUI::PaintEvent& event)
if (!m_document)
return;
- painter.translate(frame_thickness(), frame_thickness());
- painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
-
auto page = get_rendered_page(m_current_page_index);
+ set_content_size(page->size());
- auto total_width = width() - frame_thickness() * 2;
- auto total_height = height() - frame_thickness() * 2;
- auto bitmap_width = page->width();
- auto bitmap_height = page->height();
+ painter.translate(frame_thickness(), frame_thickness());
+ painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
- Gfx::IntPoint p { (total_width - bitmap_width) / 2, (total_height - bitmap_height) / 2 };
+ int x = max(0, (width() - page->width()) / 2);
+ int y = max(0, (height() - page->height()) / 2);
- painter.blit(p, *page, page->rect());
+ painter.blit({ x, y }, *page, page->rect());
}
void PDFViewer::mousewheel_event(GUI::MouseEvent& event)
{
- if (event.wheel_delta() > 0) {
- if (m_current_page_index < m_document->get_page_count() - 1)
- m_current_page_index++;
- } else if (m_current_page_index > 0) {
- m_current_page_index--;
+ bool scrolled_down = event.wheel_delta() > 0;
+
+ if (event.ctrl()) {
+ if (scrolled_down) {
+ zoom_out();
+ } else {
+ zoom_in();
+ }
+ } else {
+ auto& scrollbar = event.shift() ? horizontal_scrollbar() : vertical_scrollbar();
+
+ if (scrolled_down) {
+ if (scrollbar.value() == scrollbar.max()) {
+ if (m_current_page_index < m_document->get_page_count() - 1) {
+ m_current_page_index++;
+ scrollbar.set_value(0);
+ }
+ } else {
+ scrollbar.set_value(scrollbar.value() + 20);
+ }
+ } else {
+ if (scrollbar.value() == 0) {
+ if (m_current_page_index > 0) {
+ m_current_page_index--;
+ scrollbar.set_value(scrollbar.max());
+ }
+ } else {
+ scrollbar.set_value(scrollbar.value() - 20);
+ }
+ }
}
+
update();
}
+void PDFViewer::timer_event(Core::TimerEvent&)
+{
+ // Clear the bitmap vector of all pages except the current page
+ for (size_t i = 0; i < m_rendered_page_list.size(); i++) {
+ if (i != m_current_page_index)
+ m_rendered_page_list[i].clear();
+ }
+}
+
+void PDFViewer::zoom_in()
+{
+ if (m_zoom_level < number_of_zoom_levels - 1)
+ m_zoom_level++;
+}
+
+void PDFViewer::zoom_out()
+{
+ if (m_zoom_level > 0)
+ m_zoom_level--;
+}
+
RefPtr<Gfx::Bitmap> PDFViewer::render_page(const PDF::Page& page)
{
+ auto zoom_scale_factor = static_cast<float>(zoom_levels[m_zoom_level]) / 100.0f;
+
float page_width = page.media_box.upper_right_x - page.media_box.lower_left_x;
float page_height = page.media_box.upper_right_y - page.media_box.lower_left_y;
float page_scale_factor = page_height / page_width;
- float width = 300.0f;
+ float width = 300.0f * zoom_scale_factor;
float height = width * page_scale_factor;
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { width, height });
diff --git a/Userland/Applications/PDFViewer/PDFViewer.h b/Userland/Applications/PDFViewer/PDFViewer.h
index 528bfa12f2..66ea4eb73c 100644
--- a/Userland/Applications/PDFViewer/PDFViewer.h
+++ b/Userland/Applications/PDFViewer/PDFViewer.h
@@ -11,11 +11,35 @@
#include <LibGfx/Bitmap.h>
#include <LibPDF/Document.h>
+static constexpr u16 zoom_levels[] = {
+ 17,
+ 21,
+ 26,
+ 33,
+ 41,
+ 51,
+ 64,
+ 80,
+ 100,
+ 120,
+ 144,
+ 173,
+ 207,
+ 249,
+ 299,
+ 358,
+ 430
+};
+
+static constexpr size_t number_of_zoom_levels = sizeof(zoom_levels) / sizeof(zoom_levels[0]);
+
+static constexpr size_t initial_zoom_level = 8;
+
class PDFViewer : public GUI::AbstractScrollableWidget {
C_OBJECT(PDFViewer)
public:
- virtual ~PDFViewer() override;
+ virtual ~PDFViewer() override = default;
void set_document(RefPtr<PDF::Document>);
@@ -24,12 +48,18 @@ protected:
virtual void paint_event(GUI::PaintEvent&) override;
virtual void mousewheel_event(GUI::MouseEvent&) override;
+ virtual void timer_event(Core::TimerEvent&) override;
private:
RefPtr<Gfx::Bitmap> get_rendered_page(u32 index);
RefPtr<Gfx::Bitmap> render_page(const PDF::Page&);
+ void zoom_in();
+ void zoom_out();
+
RefPtr<PDF::Document> m_document;
u32 m_current_page_index { 0 };
- HashMap<u32, RefPtr<Gfx::Bitmap>> m_rendered_pages;
+ Vector<HashMap<u32, RefPtr<Gfx::Bitmap>>> m_rendered_page_list;
+
+ u8 m_zoom_level { initial_zoom_level };
};