1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
// 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 "kitty.hpp"
#include "dimensions.hpp"
#include "util.hpp"
#include <fmt/format.h>
#include <iostream>
#ifdef HAVE_STD_EXECUTION_H
# include <execution>
#else
# include <oneapi/tbb.h>
#endif
Kitty::Kitty(std::unique_ptr<Image> new_image, std::mutex *stdout_mutex)
: image(std::move(new_image)),
stdout_mutex(stdout_mutex),
id(util::generate_random_number<uint32_t>(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<KittyChunk>
{
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<KittyChunk> 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;
}
|