#include "../include/ImageViewer.hpp" #include "../include/Notification.hpp" #include "../include/Storage.hpp" #include "../plugins/Manga.hpp" #include #include #include namespace QuickMedia { ImageViewer::ImageViewer(Manga *manga, const std::string &images_url, const std::string &chapter_title, int current_page, const Path &chapter_cache_dir, sf::Font *font) : current_page(current_page), num_pages(0), chapter_title(chapter_title), chapter_cache_dir(chapter_cache_dir), focused_page(current_page), font(font), page_text("", *font, 14) { if(manga->get_number_of_images(images_url, num_pages) != ImageResult::OK) { show_notification("Plugin", "Failed to get number of images", Urgency::CRITICAL); return; } current_page = std::min(current_page, num_pages); image_data.resize(num_pages); page_size.resize(num_pages); for(int i = 0; i < num_pages; ++i) { image_data[i] = nullptr; page_size[i].loaded = false; } page_text.setFillColor(sf::Color::White); } bool ImageViewer::render_page(sf::RenderWindow &window, int page, double offset_y) { if(page < 0 || page >= (int)image_data.size()) return false; const sf::Vector2 image_size = get_page_size(page); std::unique_ptr &page_image_data = image_data[page]; sf::Vector2 render_pos(window_size.x * 0.5 - image_size.x * 0.5, - image_size.y * 0.5 + scroll + offset_y); if(render_pos.y + image_size.y <= 0.0 || render_pos.y >= window_size.y) { if(page_image_data) page_image_data->visible_on_screen = false; return true; } double center_dist = std::abs(window_size.y * 0.5 - (render_pos.y + image_size.y * 0.5)); if(center_dist < min_page_center_dist) { min_page_center_dist = center_dist; page_closest_to_center = page; } if(page_image_data) { if(page_image_data->failed_to_load_image) { sf::Text error_message("Failed to load image for page " + std::to_string(1 + page), *font, 30); auto text_bounds = error_message.getLocalBounds(); error_message.setFillColor(sf::Color::Black); sf::Vector2 render_pos_text(window_size.x * 0.5 - text_bounds.width * 0.5, - text_bounds.height * 0.5 + scroll + offset_y); sf::RectangleShape background(sf::Vector2f(image_size.x, image_size.y)); background.setFillColor(sf::Color::White); background.setPosition(render_pos.x, render_pos.y); window.draw(background); error_message.setPosition(render_pos_text.x, render_pos_text.y); window.draw(error_message); } else { page_image_data->sprite.setPosition(render_pos.x, render_pos.y); window.draw(page_image_data->sprite); } } else { std::string page_str = std::to_string(1 + page); sf::Text error_message("Downloading page " + page_str, *font, 30); auto text_bounds = error_message.getLocalBounds(); error_message.setFillColor(sf::Color::Black); sf::Vector2 render_pos_text(window_size.x * 0.5 - text_bounds.width * 0.5, - text_bounds.height * 0.5 + scroll + offset_y); sf::RectangleShape background(sf::Vector2f(image_size.x, image_size.y)); background.setFillColor(sf::Color::White); background.setPosition(render_pos.x, render_pos.y); window.draw(background); error_message.setPosition(render_pos_text.x, render_pos_text.y); window.draw(error_message); Path image_path = chapter_cache_dir; image_path.join(page_str); // TODO: Make image loading asynchronous if(get_file_type(image_path) == FileType::REGULAR) { fprintf(stderr, "ImageViewer: Load page %d\n", 1 + page); page_image_data = std::make_unique(); page_image_data->visible_on_screen = true; std::string image_data; if(file_get_content(image_path, image_data) == 0) { if(page_image_data->texture.loadFromMemory(image_data.data(), image_data.size())) { page_image_data->texture.setSmooth(true); page_image_data->sprite.setTexture(page_image_data->texture, true); //image_texture.generateMipmap(); page_image_data->failed_to_load_image = false; page_size[page].size = get_page_size(page); page_size[page].loaded = true; } else { page_image_data->failed_to_load_image = true; } } else { show_notification("Manga", "Failed to load image for page " + page_str + ". Image filepath: " + image_path.data, Urgency::CRITICAL); page_image_data->failed_to_load_image = true; } } } return true; } ImageViewerAction ImageViewer::draw(sf::RenderWindow &window) { const double frame_delta = frame_timer.restart().asSeconds(); const double scroll_speed_key_input = 450.0; const double scroll_speed_mouse_wheel = 600.0; const double scroll_deaccel = 0.96; if(!window_size_set) { auto window_size_i = window.getSize(); window_size.x = window_size_i.x; window_size.y = window_size_i.y; window_size_set = true; } // TODO: Only redraw when scrolling and when image has finished downloading sf::Event event; while(window.pollEvent(event)) { if (event.type == sf::Event::Closed) { //current_page = Page::EXIT; window.close(); return ImageViewerAction::RETURN; } else if(event.type == sf::Event::Resized) { window_size.x = event.size.width; window_size.y = event.size.height; sf::FloatRect visible_area(0, 0, window_size.x, window_size.y); window.setView(sf::View(visible_area)); //redraw = true; } else if(event.type == sf::Event::GainedFocus) { //redraw = true; } else if(event.type == sf::Event::KeyPressed) { if(event.key.code == sf::Keyboard::Up) { scroll_speed += scroll_speed_key_input * frame_delta; } else if(event.key.code == sf::Keyboard::Down) { scroll_speed -= scroll_speed_key_input * frame_delta; } else if(event.key.code == sf::Keyboard::Escape) { return ImageViewerAction::RETURN; } else if(event.key.code == sf::Keyboard::I) { return ImageViewerAction::SWITCH_TO_SINGLE_IMAGE_MODE; } } else if(event.type == sf::Event::MouseWheelScrolled && event.mouseWheelScroll.wheel == sf::Mouse::VerticalWheel) { scroll_speed += scroll_speed_mouse_wheel * event.mouseWheelScroll.delta * frame_delta; } } scroll += scroll_speed; scroll *= scroll_deaccel; if(std::abs(scroll) < 0.1) scroll = 0.0; min_page_center_dist = 9999999.0; page_closest_to_center = -1; const sf::Vector2 selected_page_size = get_page_size(current_page); render_page(window, current_page, window_size.y*0.5); //if(!focused_page_rendered) // return; // Render previous pages double page_offset = window_size.y*0.5 - selected_page_size.y*0.5; int page = current_page - 1; while(true) { const sf::Vector2 image_size = get_page_size(page); page_offset -= image_size.y*0.5; if(!render_page(window, page, page_offset)) break; --page; page_offset -= image_size.y*0.5; } // Render next pages page_offset = window_size.y*0.5 + selected_page_size.y*0.5; page = current_page + 1; while(true) { const sf::Vector2 image_size = get_page_size(page); page_offset += image_size.y*0.5; if(!render_page(window, page, page_offset)) break; ++page; page_offset += image_size.y*0.5; } if(page_closest_to_center != -1) { focused_page = page_closest_to_center; } if(focused_page != prev_focused_page) { prev_focused_page = focused_page; page_text.setString(chapter_title + " | Page " + std::to_string(1 + focused_page) + "/" + std::to_string(num_pages)); } const float font_height = page_text.getCharacterSize() + 8.0f; const float background_height = font_height + 6.0f; sf::RectangleShape page_text_background(sf::Vector2f(window_size.x, background_height)); page_text_background.setFillColor(sf::Color(0, 0, 0, 150)); page_text_background.setPosition(0.0f, window_size.y - background_height); window.draw(page_text_background); auto page_text_bounds = page_text.getLocalBounds(); page_text.setPosition(std::floor(window_size.x * 0.5f - page_text_bounds.width * 0.5f), std::floor(window_size.y - background_height * 0.5f - font_height * 0.5f)); window.draw(page_text); // Free pages that are not visible on the screen int i = 0; for(auto &page_data : image_data) { if(page_data && !page_data->visible_on_screen) { fprintf(stderr, "ImageViewer: Unload page %d\n", 1 + i); page_data.reset(); } ++i; } return ImageViewerAction::NONE; } int ImageViewer::get_focused_page() const { return 1 + focused_page; } sf::Vector2 ImageViewer::get_page_size(int page) { const sf::Vector2 no_image_page_size(720.0, 1280.0); if(page < 0 || page >= (int)image_data.size()) return no_image_page_size; if(page_size[page].loaded) return page_size[page].size; if(!image_data[page]) return no_image_page_size; sf::Vector2u texture_size = image_data[page]->texture.getSize(); return sf::Vector2(texture_size.x, texture_size.y); } }