diff options
| author | pants <niklasjo2004@gmail.com> | 2026-09-21 14:18:06 -0700 |
|---|---|---|
| committer | pants <niklasjo2004@gmail.com> | 2026-09-21 14:18:06 -0700 |
| commit | 83d5ba42f8a98fc0df177efef4333f74a0481f61 (patch) | |
| tree | 6f1fcff64ec9adb2fc51a7cfcc451eb22a103fb3 /src/image.cpp | |
Diffstat (limited to 'src/image.cpp')
| -rw-r--r-- | src/image.cpp | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/image.cpp b/src/image.cpp new file mode 100644 index 0000000..fae3ef6 --- /dev/null +++ b/src/image.cpp @@ -0,0 +1,185 @@ +// Display images inside a terminal +// Copyright (C) 2023 JustKidding +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +#include "image.hpp" +#ifdef ENABLE_OPENCV +# include "image/opencv.hpp" +#endif +#include "dimensions.hpp" +#include "flags.hpp" +#include "image/libvips.hpp" +#include "util.hpp" + +#ifdef ENABLE_OPENCV +# include <opencv2/imgcodecs.hpp> +#endif +#include <spdlog/spdlog.h> +#include <vips/vips.h> + +namespace fs = std::filesystem; +using njson = nlohmann::json; + +auto Image::load(const njson &command, const Terminal *terminal) -> std::unique_ptr<Image> +{ + const fs::path &filename = command.at("path"); + if (!fs::exists(filename)) { + return nullptr; + } + const auto flags = Flags::instance(); + const auto logger = spdlog::get("main"); + std::shared_ptr<Dimensions> dimensions; + try { + dimensions = get_dimensions(command, terminal); + } catch (const std::exception &) { + logger->error("Could not parse dimensions from command"); + return nullptr; + } + std::string image_path = filename; + bool in_cache = false; + if (!flags->no_cache) { + image_path = check_cache(*dimensions, filename); + in_cache = image_path != filename; + } + +#ifdef ENABLE_OPENCV + if (cv::haveImageReader(image_path) && !flags->no_opencv) { + try { + return std::make_unique<OpencvImage>(dimensions, image_path, in_cache); + } catch (const std::runtime_error &) { + return nullptr; + } + } +#endif + const auto *vips_loader = vips_foreign_find_load(image_path.c_str()); + if (vips_loader != nullptr) { + try { + return std::make_unique<LibvipsImage>(dimensions, image_path, in_cache); + } catch (const vips::VError &) { + return nullptr; + } + } + return nullptr; +} + +auto Image::check_cache(const Dimensions &dimensions, const fs::path &orig_path) -> std::string +{ + const fs::path cache_path = util::get_cache_file_save_location(orig_path); + if (!fs::exists(cache_path)) { + return orig_path; + } + + vips::VImage cache_img; + try { + cache_img = vips::VImage::new_from_file(cache_path.c_str()); + } catch (const vips::VError &) { + return orig_path; + } + const uint32_t img_width = cache_img.width(); + const uint32_t img_height = cache_img.height(); + const uint32_t dim_width = dimensions.max_wpixels(); + const uint32_t dim_height = dimensions.max_hpixels(); + const int delta = 10; + + if ((dim_width >= img_width && dim_height >= img_height) && + ((dim_width - img_width) <= delta || (dim_height - img_height) <= delta)) { + return cache_path; + } + return orig_path; +} + +auto Image::get_new_sizes(double max_width, double max_height, std::string_view scaler, int scale_factor) const + -> std::pair<int, int> +{ + int img_width = width(); + int img_height = height(); + int new_width = img_width; + int new_height = img_height; + double new_scale = 0; + double width_scale = 0; + double height_scale = 0; + double min_scale = 0; + double max_scale = 0; + + if (scaler == "fit_contain" || scaler == "forced_cover") { + // I believe these should work the same + new_scale = max_height / img_height; + if (img_width >= img_height) { + new_scale = max_width / img_width; + } + new_width = static_cast<int>(img_width * new_scale); + new_height = static_cast<int>(img_height * new_scale); + new_scale = 1; + } + + if (new_height > max_height) { + if (new_width > max_width) { + width_scale = max_width / new_width; + height_scale = max_height / new_height; + min_scale = std::min(width_scale, height_scale); + max_scale = std::max(width_scale, height_scale); + if (new_width * max_scale <= max_width && new_height * max_scale <= max_height) { + new_scale = max_scale; + } else { + new_scale = min_scale; + } + } else { + new_scale = max_height / new_height; + } + } else if (new_width > max_width) { + new_scale = max_width / new_width; + } + if (new_scale != 1) { + new_width = static_cast<int>(new_width * new_scale); + new_height = static_cast<int>(new_height * new_scale); + } + + return std::make_pair(util::round_up(new_width, scale_factor), util::round_up(new_height, scale_factor)); +} + +auto Image::get_dimensions(const njson &json, const Terminal *terminal) -> std::shared_ptr<Dimensions> +{ + using std::string; + int xcoord = 0; + int ycoord = 0; + int max_width = 0; + int max_height = 0; + string width_key = "max_width"; + string height_key = "max_height"; + const string scaler = json.value("scaler", "contain"); + if (json.contains("width")) { + width_key = "width"; + height_key = "height"; + } + if (json.at(width_key).is_string()) { + const string &width = json.at(width_key); + const string &height = json.at(height_key); + max_width = std::stoi(width); + max_height = std::stoi(height); + } else { + max_width = json.at(width_key); + max_height = json.at(height_key); + } + if (json.at("x").is_string()) { + const string &xcoords = json.at("x"); + const string &ycoords = json.at("y"); + xcoord = std::stoi(xcoords); + ycoord = std::stoi(ycoords); + } else { + xcoord = json.at("x"); + ycoord = json.at("y"); + } + return std::make_shared<Dimensions>(terminal, xcoord, ycoord, max_width, max_height, scaler); +} |
