aboutsummaryrefslogtreecommitdiff
path: root/src/AsyncImageLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/AsyncImageLoader.cpp')
-rw-r--r--src/AsyncImageLoader.cpp138
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