diff options
Diffstat (limited to 'src/AsyncImageLoader.cpp')
-rw-r--r-- | src/AsyncImageLoader.cpp | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp new file mode 100644 index 0000000..020baf1 --- /dev/null +++ b/src/AsyncImageLoader.cpp @@ -0,0 +1,138 @@ +#include "../include/AsyncImageLoader.hpp" +#include "../include/base64_url.hpp" +#include "../include/Storage.hpp" +#include "../include/DownloadUtils.hpp" +#include "../include/ImageUtils.hpp" +#include "../include/Scale.hpp" +#include <assert.h> + +namespace QuickMedia { + static sf::Vector2f to_vec2f(const sf::Vector2u &vec) { + return sf::Vector2f(vec.x, vec.y); + } + + static sf::Vector2f to_vec2f(const sf::Vector2i &vec) { + return sf::Vector2f(vec.x, vec.y); + } + + static sf::Vector2u to_vec2u(const sf::Vector2f &vec) { + return sf::Vector2u(vec.x, vec.y); + } + + static void copy_resize(const sf::Image &source, sf::Image &destination, sf::Vector2u destination_size) { + const sf::Vector2u source_size = source.getSize(); + if(source_size.x == 0 || source_size.y == 0 || destination_size.x == 0 || destination_size.y == 0) + return; + + //float width_ratio = (float)source_size.x / (float)destination_size.x; + //float height_ratio = (float)source_size.y / (float)destination_size.y; + + const sf::Uint8 *source_pixels = source.getPixelsPtr(); + // TODO: Remove this somehow. Right now we need to allocate this and also allocate the same array in the destination image + sf::Uint32 *destination_pixels = new sf::Uint32[destination_size.x * destination_size.y]; + sf::Uint32 *destination_pixel = destination_pixels; + for(unsigned int y = 0; y < destination_size.y; ++y) { + for(unsigned int x = 0; x < destination_size.x; ++x) { + int scaled_x = ((float)x / (float)destination_size.x) * source_size.x; + int scaled_y = ((float)y / (float)destination_size.y) * source_size.y; + //float scaled_x = x * width_ratio; + //float scaled_y = y * height_ratio; + + //sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (int)(scaled_x + scaled_y * source_size.x) * 4); + sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (scaled_x + scaled_y * source_size.x) * 4); + *destination_pixel = *source_pixel; + ++destination_pixel; + } + } + destination.create(destination_size.x, destination_size.y, (sf::Uint8*)destination_pixels); + delete []destination_pixels; + } + + static bool save_image_as_thumbnail_atomic(const sf::Image &image, const Path &thumbnail_path, const char *ext) { + Path tmp_path = thumbnail_path; + tmp_path.append(".tmp"); + const char *thumbnail_path_ext = thumbnail_path.ext(); + if(is_image_ext(ext)) + tmp_path.append(ext); + else if(is_image_ext(thumbnail_path_ext)) + tmp_path.append(thumbnail_path_ext); + else + tmp_path.append(".png"); + return image.saveToFile(tmp_path.data) && (rename(tmp_path.data.c_str(), thumbnail_path.data.c_str()) == 0); + } + + // Returns empty string if no extension + static const char* get_ext(const std::string &path) { + size_t index = path.rfind('.'); + if(index == std::string::npos) + return ""; + return path.c_str() + index; + } + + bool AsyncImageLoader::load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, bool use_tor, std::shared_ptr<ThumbnailData> thumbnail_data) { + update(); + + if(loading_image) + return false; + + loading_image = true; + + assert(thumbnail_data->loading_state == LoadingState::NOT_LOADED); + thumbnail_data->loading_state = LoadingState::LOADING; + + if(url.empty()) { + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + loading_image = false; + return true; + } + + load_image_future = std::async(std::launch::async, [url, local, resize_target_size, thumbnail_data, use_tor]() mutable { + // TODO: Use sha256 instead of base64_url encoding + Path thumbnail_path = get_cache_dir().join("thumbnails").join(base64_url::encode(url)); + + thumbnail_data->image = std::make_unique<sf::Image>(); + if(thumbnail_data->image->loadFromFile(thumbnail_path.data)) { + fprintf(stderr, "Loaded %s from thumbnail cache\n", url.c_str()); + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; + } else { + if(local) { + if(!thumbnail_data->image->loadFromFile(url)) { + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; + } + } else { + std::string texture_data; + if(download_to_string_cache(url, texture_data, {}, use_tor, true) != DownloadResult::OK || !thumbnail_data->image->loadFromMemory(texture_data.data(), texture_data.size())) { + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; + } + } + } + + if(resize_target_size.x != 0 && resize_target_size.y != 0) { + sf::Vector2u new_image_size = to_vec2u(clamp_to_size(to_vec2f(thumbnail_data->image->getSize()), to_vec2f(resize_target_size))); + if(new_image_size.x < thumbnail_data->image->getSize().x || new_image_size.y < thumbnail_data->image->getSize().y) { + auto destination_image = std::make_unique<sf::Image>(); + copy_resize(*thumbnail_data->image, *destination_image, new_image_size); + thumbnail_data->image = std::move(destination_image); + save_image_as_thumbnail_atomic(*thumbnail_data->image, thumbnail_path, get_ext(url)); + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; + } + } + + thumbnail_data->loading_state = LoadingState::FINISHED_LOADING; + return; + }); + + return true; + } + + void AsyncImageLoader::update() { + if(loading_image && load_image_future.valid() && load_image_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { + load_image_future.get(); + loading_image = false; + } + } +}
\ No newline at end of file |