aboutsummaryrefslogtreecommitdiff
path: root/src/AsyncImageLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/AsyncImageLoader.cpp')
-rw-r--r--src/AsyncImageLoader.cpp187
1 files changed, 70 insertions, 117 deletions
diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp
index 9792781..c096e31 100644
--- a/src/AsyncImageLoader.cpp
+++ b/src/AsyncImageLoader.cpp
@@ -8,90 +8,34 @@
#include <assert.h>
namespace QuickMedia {
- // Linear interpolation
- // TODO: Is this implementation ok? it always uses +1 offset for interpolation but if we were to resize an image with near 1x1 scaling
- // then it would be slightly blurry
- 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;
+ bool create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, sf::Vector2i resize_target_size) {
+ // > 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) + ">";
- //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_start = ((float)x / (float)destination_size.x) * source_size.x;
- int scaled_y_start = ((float)y / (float)destination_size.y) * source_size.y;
- int scaled_x_end = ((float)(x + 1) / (float)destination_size.x) * source_size.x;
- int scaled_y_end = ((float)(y + 1) / (float)destination_size.y) * source_size.y;
- if(scaled_x_end > (int)source_size.x - 1) scaled_x_end = source_size.x - 1;
- if(scaled_y_end > (int)source_size.y - 1) scaled_y_end = source_size.y - 1;
- //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 sum_red = 0;
- sf::Uint32 sum_green = 0;
- sf::Uint32 sum_blue = 0;
- sf::Uint32 sum_alpha = 0;
- sf::Uint32 num_colors = (scaled_x_end - scaled_x_start) * (scaled_y_end - scaled_y_start);
- for(int yy = scaled_y_start; yy < scaled_y_end; ++yy) {
- for(int xx = scaled_x_start; xx < scaled_x_end; ++xx) {
- sf::Uint32 *source_pixel = (sf::Uint32*)(source_pixels + (xx + yy * source_size.x) * 4);
- sum_red += (*source_pixel >> 24);
- sum_green += ((*source_pixel >> 16) & 0xFF);
- sum_blue += ((*source_pixel >> 8) & 0xFF);
- sum_alpha += (*source_pixel & 0xFF);
- }
- }
- sum_red /= num_colors;
- sum_green /= num_colors;
- sum_blue /= num_colors;
- sum_alpha /= num_colors;
- *destination_pixel = (sum_red << 24) | (sum_green << 16) | (sum_blue << 8) | sum_alpha;
- ++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);
- }
+ // We only want the first frame if its a gif
+ Path thumbnail_path_first_frame = thumbnail_path;
+ thumbnail_path_first_frame.append("[0]");
+
+ Path result_path_tmp = thumbnail_path_resized;
+ result_path_tmp.append(".tmp");
- // Returns empty string if no extension
- static const char* get_ext(const std::string &path) {
- size_t slash_index = path.rfind('/');
- size_t index = path.rfind('.');
- if(index != std::string::npos && (slash_index == std::string::npos || index > slash_index))
- return path.c_str() + index;
- return "";
+ 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(convert_res == 0 && rename(result_path_tmp.data.c_str(), thumbnail_path_resized.data.c_str()) == 0)
+ return true;
+ else
+ return false;
}
- static void create_thumbnail_if_thumbnail_smaller_than_image(const std::string &original_url, const Path &thumbnail_path, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) {
- sf::Vector2u new_image_size = clamp_to_size(thumbnail_data->image->getSize(), sf::Vector2u(resize_target_size.x, resize_target_size.y));
- 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(original_url));
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ // Create thumbnail and load it. On failure load the original image
+ static void create_thumbnail(const Path &thumbnail_path, const Path &thumbnail_path_resized, ThumbnailData *thumbnail_data, sf::Vector2i resize_target_size) {
+ if(create_thumbnail(thumbnail_path, thumbnail_path_resized, resize_target_size)) {
+ load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data);
+ } else {
+ load_image_from_file(*thumbnail_data->image, thumbnail_path.data);
+ fprintf(stderr, "Failed to convert %s to a thumbnail, using the original image\n", thumbnail_path.data.c_str());
}
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
}
AsyncImageLoader& AsyncImageLoader::get_instance() {
@@ -106,7 +50,7 @@ namespace QuickMedia {
loading_image[i] = false;
}
- load_image_thread = std::thread([this]{
+ load_image_thread = std::thread([this]() mutable {
std::optional<ThumbnailLoadData> thumbnail_load_data_opt;
while(true) {
thumbnail_load_data_opt = image_load_queue.pop_wait();
@@ -114,24 +58,31 @@ namespace QuickMedia {
break;
ThumbnailLoadData &thumbnail_load_data = thumbnail_load_data_opt.value();
-
thumbnail_load_data.thumbnail_data->image = std::make_unique<sf::Image>();
- if(load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_load_data.thumbnail_path.data)) {
- fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_load_data.path.data.c_str());
+
+ Path thumbnail_path_resized = thumbnail_load_data.thumbnail_path;
+ if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0)
+ thumbnail_path_resized.append("_" + std::to_string(thumbnail_load_data.resize_target_size.x) + "x" + std::to_string(thumbnail_load_data.resize_target_size.y));
+
+ if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) {
+ load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_path_resized.data);
thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str());
continue;
}
- if(thumbnail_load_data.local) {
- if(load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_load_data.path.data)
- && thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0)
- {
- create_thumbnail_if_thumbnail_smaller_than_image(thumbnail_load_data.path.data, thumbnail_load_data.thumbnail_path, thumbnail_load_data.thumbnail_data.get(), thumbnail_load_data.resize_target_size);
- }
- thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- } else {
- thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- }
+ Path thumbnail_original_path;
+ if(thumbnail_load_data.local)
+ thumbnail_original_path = thumbnail_load_data.path;
+ else
+ thumbnail_original_path = thumbnail_load_data.thumbnail_path;
+
+ if(thumbnail_load_data.resize_target_size.x != 0 && thumbnail_load_data.resize_target_size.y != 0)
+ create_thumbnail(thumbnail_original_path, thumbnail_path_resized, thumbnail_load_data.thumbnail_data.get(), thumbnail_load_data.resize_target_size);
+ else
+ load_image_from_file(*thumbnail_load_data.thumbnail_data->image, thumbnail_original_path.data);
+
+ thumbnail_load_data.thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
}
});
}
@@ -164,15 +115,13 @@ namespace QuickMedia {
SHA256 sha256;
sha256.add(url.data(), url.size());
Path thumbnail_path = get_cache_dir().join("thumbnails").join(sha256.getHash());
- if(resize_target_size.x != 0 && resize_target_size.y != 0)
- thumbnail_path.append("_" + std::to_string(resize_target_size.x) + "x" + std::to_string(resize_target_size.y));
- if(get_file_type(thumbnail_path) == FileType::REGULAR) {
+ if(local && get_file_type(url) == FileType::REGULAR) {
thumbnail_data->loading_state = LoadingState::LOADING;
- image_load_queue.push({ url, thumbnail_path, local, thumbnail_data, resize_target_size });
+ image_load_queue.push({ url, thumbnail_path, true, thumbnail_data, resize_target_size });
return;
- } else if(local && get_file_type(url) == FileType::REGULAR) {
+ } else if(get_file_type(thumbnail_path) == FileType::REGULAR) {
thumbnail_data->loading_state = LoadingState::LOADING;
- image_load_queue.push({ url, thumbnail_path, true, thumbnail_data, resize_target_size });
+ image_load_queue.push({ url, thumbnail_path, false, thumbnail_data, resize_target_size });
return;
}
@@ -188,30 +137,34 @@ namespace QuickMedia {
// TODO: Keep the thread running and use conditional variable instead to sleep until a new image should be loaded. Same in ImageViewer.
download_image_thread[free_index] = std::thread([this, free_index, thumbnail_path, url, local, resize_target_size, thumbnail_data, use_tor]() mutable {
thumbnail_data->image = std::make_unique<sf::Image>();
- if(load_image_from_file(*thumbnail_data->image, thumbnail_path.data)) {
- fprintf(stderr, "Loaded %s from thumbnail cache\n", url.c_str());
+
+ Path thumbnail_path_resized = thumbnail_path;
+ if(resize_target_size.x != 0 && resize_target_size.y != 0)
+ thumbnail_path_resized.append("_" + std::to_string(resize_target_size.x) + "x" + std::to_string(resize_target_size.y));
+
+ if(get_file_type(thumbnail_path_resized) == FileType::REGULAR) {
+ load_image_from_file(*thumbnail_data->image, thumbnail_path_resized.data);
+ thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
+ fprintf(stderr, "Loaded %s from thumbnail cache\n", thumbnail_path_resized.data.c_str());
+ return;
+ }
+
+ if(!local && get_file_type(thumbnail_path.data) != FileType::REGULAR && download_to_file(url, thumbnail_path.data, {}, use_tor, true) != DownloadResult::OK) {
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
loading_image[free_index] = false;
return;
- } else {
- if(local) {
- if(!load_image_from_file(*thumbnail_data->image, url)) {
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- loading_image[free_index] = false;
- return;
- }
- } else {
- // Use the same path as the thumbnail path, since it wont be overwritten if the image is smaller than the thumbnail target size
- if(download_to_file(url, thumbnail_path.data, {}, use_tor, true) != DownloadResult::OK || !load_image_from_file(*thumbnail_data->image, thumbnail_path.data)) {
- thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
- loading_image[free_index] = false;
- return;
- }
- }
}
+ Path thumbnail_original_path;
+ if(local)
+ thumbnail_original_path = url;
+ else
+ thumbnail_original_path = thumbnail_path;
+
if(resize_target_size.x != 0 && resize_target_size.y != 0)
- create_thumbnail_if_thumbnail_smaller_than_image(url, thumbnail_path, thumbnail_data.get(), resize_target_size);
+ create_thumbnail(thumbnail_original_path, thumbnail_path_resized, thumbnail_data.get(), resize_target_size);
+ else
+ load_image_from_file(*thumbnail_data->image, thumbnail_original_path.data);
thumbnail_data->loading_state = LoadingState::FINISHED_LOADING;
loading_image[free_index] = false;