aboutsummaryrefslogtreecommitdiff
path: root/src/Body.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Body.cpp')
-rw-r--r--src/Body.cpp142
1 files changed, 129 insertions, 13 deletions
diff --git a/src/Body.cpp b/src/Body.cpp
index 20911de..ae60da2 100644
--- a/src/Body.cpp
+++ b/src/Body.cpp
@@ -1,5 +1,8 @@
#include "../include/Body.hpp"
#include "../include/QuickMedia.hpp"
+#include "../include/Scale.hpp"
+#include "../include/base64_url.hpp"
+#include "../include/ImageUtils.hpp"
#include "../plugins/Plugin.hpp"
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/Sprite.hpp>
@@ -11,7 +14,7 @@ const sf::Color front_color(43, 45, 47);
const sf::Color back_color(33, 35, 37);
namespace QuickMedia {
- BodyItem::BodyItem(std::string _title): visible(true), dirty(false), dirty_description(false), background_color(front_color) {
+ BodyItem::BodyItem(std::string _title): visible(true), dirty(false), dirty_description(false), thumbnail_is_local(false), background_color(front_color) {
set_title(std::move(_title));
}
@@ -25,6 +28,7 @@ namespace QuickMedia {
visible = other.visible;
dirty = other.dirty;
dirty_description = other.dirty_description;
+ thumbnail_is_local = other.thumbnail_is_local;
if(other.title_text)
title_text = std::make_unique<Text>(*other.title_text);
else
@@ -52,6 +56,10 @@ namespace QuickMedia {
progress_text.setFillColor(sf::Color::White);
author_text.setFillColor(sf::Color::White);
replies_text.setFillColor(sf::Color(129, 162, 190));
+ thumbnail_resize_target_size.x = 200;
+ thumbnail_resize_target_size.y = 119;
+ thumbnail_fallback_size.x = 50.0f;
+ thumbnail_fallback_size.y = 100.0f;
}
bool Body::select_previous_item() {
@@ -176,18 +184,121 @@ namespace QuickMedia {
}
}
- std::shared_ptr<sf::Texture> Body::load_thumbnail_from_url(const std::string &url) {
+ 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;
+ }
+
+ // TODO: Do not load thumbnails for images larger than 30mb
+ std::shared_ptr<sf::Texture> Body::load_thumbnail_from_url(const std::string &url, bool local, sf::Vector2i thumbnail_resize_target_size) {
auto result = std::make_shared<sf::Texture>();
result->setSmooth(true);
assert(!loading_thumbnail);
loading_thumbnail = true;
- thumbnail_load_thread = std::thread([this, result, url]() {
+ thumbnail_load_thread = std::thread([this, result, url, local, thumbnail_resize_target_size]() {
+ // TODO: Use sha256 instead of base64_url encoding
+ Path thumbnail_path = get_cache_dir().join("thumbnails").join(base64_url::encode(url));
+
std::string texture_data;
- if(download_to_string_cache(url, texture_data, {}, program->get_current_plugin()->use_tor, true) == DownloadResult::OK) {
- if(result->loadFromMemory(texture_data.data(), texture_data.size())) {
- //result->generateMipmap();
+ if(file_get_content(thumbnail_path, texture_data) == 0) {
+ fprintf(stderr, "Loaded %s from thumbnail cache\n", url.c_str());
+ result->loadFromMemory(texture_data.data(), texture_data.size());
+ loading_thumbnail = false;
+ return;
+ } else {
+ if(local) {
+ if(file_get_content(url, texture_data) != 0) {
+ loading_thumbnail = false;
+ return;
+ }
+ } else {
+ if(download_to_string_cache(url, texture_data, {}, program->get_current_plugin()->use_tor, true) != DownloadResult::OK) {
+ loading_thumbnail = false;
+ return;
+ }
}
}
+
+ if(thumbnail_resize_target_size.x != 0 && thumbnail_resize_target_size.y != 0) {
+ auto image = std::make_unique<sf::Image>();
+ // TODO: Load from file instead? decreases ram usage and we save to file above anyways
+ if(image->loadFromMemory(texture_data.data(), texture_data.size())) {
+ texture_data.resize(0);
+ sf::Vector2u new_image_size = to_vec2u(clamp_to_size(to_vec2f(image->getSize()), to_vec2f(thumbnail_resize_target_size)));
+ if(new_image_size.x < image->getSize().x || new_image_size.y < image->getSize().y) {
+ sf::Image destination_image;
+ copy_resize(*image, destination_image, new_image_size);
+ image.reset();
+ if(save_image_as_thumbnail_atomic(destination_image, thumbnail_path, get_ext(url)))
+ result->loadFromImage(destination_image);
+ loading_thumbnail = false;
+ return;
+ } else {
+ result->loadFromImage(*image);
+ loading_thumbnail = false;
+ return;
+ }
+ }
+ }
+
+ result->loadFromMemory(texture_data.data(), texture_data.size());
loading_thumbnail = false;
});
thumbnail_load_thread.detach();
@@ -207,16 +318,20 @@ namespace QuickMedia {
sf::Vector2f scissor_size = size;
size.x = std::max(0.0f, size.x - 5);
- const float image_max_height = 100.0f;
+ float image_max_height = 100.0f;
const float spacing_y = 15.0f;
const float padding_x = 10.0f;
const float image_padding_x = 5.0f;
const float padding_y = 5.0f;
const float start_y = pos.y;
- sf::RectangleShape image_fallback(sf::Vector2f(50, image_max_height));
+ sf::RectangleShape image_fallback(thumbnail_fallback_size);
image_fallback.setFillColor(sf::Color::White);
+ if(thumbnail_resize_target_size.x != 0 && thumbnail_resize_target_size.y != 0) {
+ image_max_height = thumbnail_resize_target_size.y;
+ }
+
sf::Sprite image;
sf::RectangleShape item_background;
@@ -278,7 +393,7 @@ namespace QuickMedia {
if(draw_thumbnails && !item->thumbnail_url.empty()) {
auto &item_thumbnail = item_thumbnail_textures[item->thumbnail_url];
item_thumbnail.referenced = false;
- float image_height = image_max_height;
+ float image_height = image_fallback.getSize().y;
if(item_thumbnail.texture && item_thumbnail.texture->getNativeHandle() != 0) {
auto image_size = item_thumbnail.texture->getSize();
image_height = std::min(image_max_height, (float)image_size.y);
@@ -325,8 +440,8 @@ namespace QuickMedia {
item_height += item->description_text->getHeight();
}
if(draw_thumbnails && !item->thumbnail_url.empty()) {
- float image_height = image_max_height;
- if(item_thumbnail.texture && item_thumbnail.texture->getNativeHandle() != 0) {
+ float image_height = image_fallback.getSize().y;
+ if(item_thumbnail.loaded && item_thumbnail.texture && item_thumbnail.texture->getNativeHandle() != 0) {
auto image_size = item_thumbnail.texture->getSize();
image_height = std::min(image_max_height, (float)image_size.y);
}
@@ -335,8 +450,9 @@ namespace QuickMedia {
item_height += (padding_y * 2.0f);
if(draw_thumbnails) {
- if(!item->thumbnail_url.empty() && !loading_thumbnail && !item_thumbnail.texture) {
- item_thumbnail.texture = load_thumbnail_from_url(item->thumbnail_url);
+ if(!item->thumbnail_url.empty() && !loading_thumbnail && !item_thumbnail.loaded && !item_thumbnail.texture) {
+ item_thumbnail.loaded = true;
+ item_thumbnail.texture = load_thumbnail_from_url(item->thumbnail_url, item->thumbnail_is_local, thumbnail_resize_target_size);
}
}