// 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 .
#include "kitty.hpp"
#include "dimensions.hpp"
#include "util.hpp"
#include
#include
#ifdef HAVE_STD_EXECUTION_H
# include
#else
# include
#endif
Kitty::Kitty(std::unique_ptr new_image, std::mutex *stdout_mutex)
: image(std::move(new_image)),
stdout_mutex(stdout_mutex),
id(util::generate_random_number(1))
{
const auto dims = image->dimensions();
x = dims.x + 1;
y = dims.y + 1;
}
Kitty::~Kitty()
{
const std::scoped_lock lock{*stdout_mutex};
std::cout << fmt::format("\033_Ga=d,d=i,i={}\033\\", id) << std::flush;
}
void Kitty::draw()
{
generate_frame();
}
void Kitty::generate_frame()
{
const int bits_per_channel = 8;
auto chunks = process_chunks();
str.append(fmt::format("\033_Ga=T,m=1,i={},q=2,f={},s={},v={};{}\033\\", id, image->channels() * bits_per_channel,
image->width(), image->height(), chunks.front().get_result()));
for (auto chunk = std::next(std::begin(chunks)); chunk != std::prev(std::end(chunks)); std::advance(chunk, 1)) {
str.append("\033_Gm=1,q=2;");
str.append(chunk->get_result());
str.append("\033\\");
}
str.append("\033_Gm=0,q=2;");
str.append(chunks.back().get_result());
str.append("\033\\");
const std::scoped_lock lock{*stdout_mutex};
util::save_cursor_position();
util::move_cursor(y, x);
std::cout << str << std::flush;
util::restore_cursor_position();
str.clear();
}
auto Kitty::process_chunks() -> std::vector
{
const uint64_t chunk_size = 3068;
uint64_t num_chunks = image->size() / chunk_size;
uint64_t last_chunk_size = image->size() % chunk_size;
if (last_chunk_size == 0) {
last_chunk_size = chunk_size;
num_chunks--;
}
const uint64_t bytes_per_chunk = 4 * ((chunk_size + 2) / 3) + 100;
str.reserve((num_chunks + 2) * bytes_per_chunk);
std::vector chunks;
chunks.reserve(num_chunks + 2);
const auto *ptr = image->data();
uint64_t idx = 0;
for (; idx < num_chunks; idx++) {
chunks.emplace_back(ptr + idx * chunk_size, chunk_size);
}
chunks.emplace_back(ptr + idx * chunk_size, last_chunk_size);
#ifdef HAVE_STD_EXECUTION_H
std::for_each(std::execution::par_unseq, std::begin(chunks), std::end(chunks), KittyChunk::process_chunk);
#else
oneapi::tbb::parallel_for_each(std::begin(chunks), std::end(chunks), KittyChunk());
#endif
return chunks;
}