From 97df498b30d8580d4b74582a634562f996003dd0 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 11 Jul 2021 20:59:58 +0200 Subject: Remove dependency on imagemagick. Use stb resize to downscale image. --- src/AsyncImageLoader.cpp | 105 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 23 deletions(-) (limited to 'src/AsyncImageLoader.cpp') diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 9e8d090..3e00459 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -6,48 +6,107 @@ #include "../include/SfmlFixes.hpp" #include "../external/hash-library/sha256.h" +#include +#include #include +#include +#include #include #include +#include + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "../external/stb/stb_image_resize.h" namespace QuickMedia { + static bool webp_to_png(const Path &thumbnail_path, const Path &destination_path) { + const char *args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", thumbnail_path.data.c_str(), "--", destination_path.data.c_str(), nullptr}; + int res = exec_program(args, nullptr, nullptr); + if(res != 0) + return false; + + return true; + } + bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, sf::Vector2i resize_target_size, ContentType content_type) { Path input_path = thumbnail_path; - // TODO: Remove this when imagemagick supports webp - // Convert webp to png - bool remove_tmp_input = false; if(content_type == ContentType::IMAGE_WEBP) { Path result_path_tmp = thumbnail_path_resized; result_path_tmp.append(".tmp.png"); - - const char *args[] = { "ffmpeg", "-y", "-v", "quiet", "-i", input_path.data.c_str(), "--", result_path_tmp.data.c_str(), nullptr}; - int res = exec_program(args, nullptr, nullptr); - if(res != 0) + if(!webp_to_png(thumbnail_path, result_path_tmp)) return false; - input_path = std::move(result_path_tmp); - remove_tmp_input = true; } - // > is to only shrink image if smaller than the target size - std::string new_size = std::to_string(resize_target_size.x) + "x" + std::to_string(resize_target_size.y) + ">"; + // Fork here because we want the memory allocated to be completely deallocated. + // TODO: Find a way to do that without fork. + pid_t parent_pid = getpid(); + pid_t pid = fork(); + if(pid == -1) { + perror("Failed to fork"); + return false; + } else if(pid == 0) { // child + if(prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + perror("prctl(PR_SET_PDEATHSIG, SIGTERM) failed"); + _exit(127); + } + + /* Test if the parent died before the above call to prctl */ + if(getppid() != parent_pid) + _exit(127); + + sf::Image image; + if(!image.loadFromFile(input_path.data) || image.getSize().x == 0 || image.getSize().y == 0) { + fprintf(stderr, "Failed to load %s\n", input_path.data.c_str()); + _exit(1); + } + + Path result_path_tmp = thumbnail_path_resized; + result_path_tmp.append(".tmp.png"); + + if(image.getSize().x <= (unsigned int)resize_target_size.x && image.getSize().y <= (unsigned int)resize_target_size.y) { + int res = symlink(input_path.data.c_str(), result_path_tmp.data.c_str()); + if(res == -1 && errno != EEXIST) { + fprintf(stderr, "Failed to save %s\n", thumbnail_path_resized.data.c_str()); + _exit(1); + } + } else { + sf::Vector2u clamped_size = clamp_to_size(image.getSize(), sf::Vector2u(resize_target_size.x, resize_target_size.y)); + unsigned char *output_pixels = new unsigned char[clamped_size.x * clamped_size.y * 4]; + stbir_resize_uint8(image.getPixelsPtr(), image.getSize().x, image.getSize().y, 0, output_pixels, clamped_size.x, clamped_size.y, 0, 4); + + // TODO: Remove this and use stb write to remove this unecessary extra copy of the data and write the data directly to file after converting it to png + sf::Image destination_image; + destination_image.create(clamped_size.x, clamped_size.y, output_pixels); + if(!destination_image.saveToFile(result_path_tmp.data)) { + fprintf(stderr, "Failed to save %s\n", thumbnail_path_resized.data.c_str()); + _exit(1); + } + } + + if(rename_atomic(result_path_tmp.data.c_str(), thumbnail_path_resized.data.c_str()) == 0) + _exit(0); + else + _exit(1); + } + + // parent - // We only want the first frame if its a gif - Path thumbnail_path_first_frame = input_path; - thumbnail_path_first_frame.append("[0]"); + int status = 0; + if(waitpid(pid, &status, 0) == -1) { + perror("waitpid failed"); + return false; + } - Path result_path_tmp = thumbnail_path_resized; - result_path_tmp.append(".tmp"); + if(!WIFEXITED(status)) + return false; - const char *args[] = { "convert", thumbnail_path_first_frame.data.c_str(), "-thumbnail", new_size.c_str(), result_path_tmp.data.c_str(), nullptr}; - int convert_res = exec_program(args, nullptr, nullptr); - if(remove_tmp_input) - remove(input_path.data.c_str()); - if(convert_res == 0 && rename_atomic(result_path_tmp.data.c_str(), thumbnail_path_resized.data.c_str()) == 0) - return true; - else + int exit_status = WEXITSTATUS(status); + if(exit_status != 0) return false; + + return true; } // Create thumbnail and load it. On failure load the original image -- cgit v1.2.3