2021-03-21 12:38:12 +01:00
|
|
|
#include "image.h"
|
2023-08-02 23:03:18 -04:00
|
|
|
#include "logger.h"
|
|
|
|
|
#include "core/config.h"
|
2021-12-22 21:00:11 +01:00
|
|
|
#include <cstring>
|
|
|
|
|
#include <limits>
|
2022-09-19 22:20:30 +02:00
|
|
|
#include <fstream>
|
2021-03-21 12:38:12 +01:00
|
|
|
|
|
|
|
|
namespace image
|
|
|
|
|
{
|
2021-11-04 18:50:24 +01:00
|
|
|
template <typename T>
|
2022-01-26 11:00:27 +01:00
|
|
|
void Image<T>::init(size_t width, size_t height, int channels)
|
2021-03-21 12:38:12 +01:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
// Reset image if we already had one
|
|
|
|
|
if (has_data)
|
|
|
|
|
delete[] d_data;
|
2021-03-21 12:38:12 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
// Init buffer
|
|
|
|
|
data_size = width * height * channels;
|
|
|
|
|
d_data = new T[data_size];
|
2021-03-21 12:38:12 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
// Set to 0
|
|
|
|
|
memset(d_data, 0, sizeof(T) * data_size);
|
2021-03-21 12:38:12 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
// Init local variables
|
|
|
|
|
d_depth = sizeof(T) * 8;
|
|
|
|
|
d_width = width;
|
|
|
|
|
d_height = height;
|
|
|
|
|
d_channels = channels;
|
2021-03-21 12:38:12 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
// We have data now if we didn't already
|
|
|
|
|
has_data = true;
|
2021-03-21 12:38:12 +01:00
|
|
|
}
|
2021-04-17 22:33:48 +02:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::clear()
|
2021-04-18 00:24:55 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
// Reset image
|
|
|
|
|
if (has_data)
|
|
|
|
|
delete[] d_data;
|
|
|
|
|
has_data = false;
|
|
|
|
|
}
|
2021-04-18 00:24:55 +02:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
Image<T>::Image()
|
2021-04-17 22:33:48 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
// Do nothing
|
2021-04-17 22:33:48 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
2022-01-26 11:00:27 +01:00
|
|
|
Image<T>::Image(size_t width, size_t height, int channels)
|
2021-07-24 17:53:52 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
init(width, height, channels);
|
2021-07-24 17:53:52 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
Image<T>::Image(const Image &img)
|
2021-04-17 22:33:48 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
// Copy contents of the image over
|
|
|
|
|
init(img.d_width, img.d_height, img.d_channels);
|
|
|
|
|
memcpy(d_data, img.d_data, img.data_size * sizeof(T));
|
2021-04-17 22:33:48 +02:00
|
|
|
}
|
2021-05-28 18:38:54 +02:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
2022-01-26 11:00:27 +01:00
|
|
|
Image<T>::Image(T *buffer, size_t width, size_t height, int channels)
|
2021-11-14 21:11:43 +01:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
// Copy contents of the image over
|
|
|
|
|
init(width, height, channels);
|
|
|
|
|
memcpy(d_data, buffer, data_size * sizeof(T));
|
|
|
|
|
}
|
2021-11-14 21:11:43 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
Image<T> &Image<T>::operator=(const Image<T> &img)
|
|
|
|
|
{
|
|
|
|
|
// Copy contents of the image over
|
|
|
|
|
init(img.d_width, img.d_height, img.d_channels);
|
|
|
|
|
memcpy(d_data, img.d_data, img.data_size * sizeof(T));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2021-11-14 21:11:43 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
Image<T> &Image<T>::operator<<=(const int &shift)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < data_size; i++)
|
|
|
|
|
d_data[i] <<= shift;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
2021-11-14 21:11:43 +01:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
Image<T>::~Image()
|
|
|
|
|
{
|
|
|
|
|
if (has_data)
|
|
|
|
|
delete[] d_data;
|
2023-08-06 01:26:41 +02:00
|
|
|
|
|
|
|
|
if (has_font)
|
|
|
|
|
{
|
2023-03-07 00:47:44 +01:00
|
|
|
font.chars.clear();
|
2023-03-08 01:02:55 +01:00
|
|
|
delete[] ttf_buffer;
|
2023-03-07 00:47:44 +01:00
|
|
|
}
|
2021-11-14 21:11:43 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-08 00:03:32 +01:00
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::init_font(std::string font_path)
|
|
|
|
|
{
|
2023-05-07 12:47:19 -07:00
|
|
|
std::ifstream infile(font_path, std::ios::binary);
|
2023-04-13 21:05:19 +02:00
|
|
|
|
|
|
|
|
if (!infile.good())
|
|
|
|
|
return;
|
2023-03-08 00:03:32 +01:00
|
|
|
// get length of file
|
|
|
|
|
infile.seekg(0, std::ios::end);
|
|
|
|
|
size_t length = infile.tellg();
|
|
|
|
|
infile.seekg(0, std::ios::beg);
|
2023-03-08 01:02:55 +01:00
|
|
|
ttf_buffer = new uint8_t[length];
|
2023-03-08 00:03:32 +01:00
|
|
|
// read file
|
|
|
|
|
infile.read((char *)ttf_buffer, length);
|
|
|
|
|
|
|
|
|
|
stbtt_fontinfo fontp;
|
|
|
|
|
stbtt_InitFont(&fontp, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0));
|
|
|
|
|
|
|
|
|
|
stbtt_GetFontBoundingBox(&fontp, &font.x0, &font.y0, &font.x1, &font.y1);
|
|
|
|
|
stbtt_GetFontVMetrics(&fontp, &font.asc, &font.dsc, &font.lg);
|
|
|
|
|
|
|
|
|
|
font.fontp = fontp;
|
|
|
|
|
infile.close();
|
|
|
|
|
has_font = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
T Image<T>::clamp(int input)
|
2021-05-28 18:38:54 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
if (input > std::numeric_limits<T>::max())
|
|
|
|
|
return std::numeric_limits<T>::max();
|
|
|
|
|
else if (input < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return input;
|
|
|
|
|
}
|
2021-05-28 18:38:54 +02:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::to_rgb()
|
|
|
|
|
{
|
|
|
|
|
if (d_channels == 1)
|
2021-05-28 18:38:54 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
Image<T> tmp = *this; // Backup image
|
|
|
|
|
init(d_width, d_height, 3); // Init new image as RGB
|
2021-05-28 18:38:54 +02:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
// Fill in all 3 channels
|
|
|
|
|
draw_image(0, tmp);
|
|
|
|
|
draw_image(1, tmp);
|
|
|
|
|
draw_image(2, tmp);
|
2021-05-28 18:38:54 +02:00
|
|
|
}
|
2022-09-09 22:55:13 +02:00
|
|
|
else if (d_channels == 4)
|
|
|
|
|
{
|
|
|
|
|
Image<T> tmp = *this; // Backup image
|
|
|
|
|
init(d_width, d_height, 3); // Init new image as RGB
|
|
|
|
|
|
|
|
|
|
// Copy over all 3 channels
|
|
|
|
|
memcpy(d_data, tmp.data(), d_width * d_height * 3 * sizeof(T));
|
|
|
|
|
}
|
2021-05-28 18:38:54 +02:00
|
|
|
}
|
2021-06-15 22:47:00 +02:00
|
|
|
|
2022-09-23 20:46:41 +02:00
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::to_rgba()
|
|
|
|
|
{
|
|
|
|
|
if (d_channels == 1)
|
|
|
|
|
{
|
|
|
|
|
Image<T> tmp = *this; // Backup image
|
|
|
|
|
init(d_width, d_height, 4); // Init new image as RGBA
|
|
|
|
|
|
2022-10-16 22:02:25 +02:00
|
|
|
// Copy over all 3 channels
|
|
|
|
|
memcpy(&d_data[d_width * d_height * 0], tmp.data(), d_width * d_height * sizeof(T));
|
|
|
|
|
memcpy(&d_data[d_width * d_height * 1], tmp.data(), d_width * d_height * sizeof(T));
|
|
|
|
|
memcpy(&d_data[d_width * d_height * 2], tmp.data(), d_width * d_height * sizeof(T));
|
2022-09-23 20:46:41 +02:00
|
|
|
for (size_t i = 0; i < d_width * d_height; i++)
|
|
|
|
|
channel(3)[i] = std::numeric_limits<T>::max();
|
|
|
|
|
}
|
|
|
|
|
else if (d_channels == 3)
|
|
|
|
|
{
|
|
|
|
|
Image<T> tmp = *this; // Backup image
|
|
|
|
|
init(d_width, d_height, 4); // Init new image as RGBA
|
|
|
|
|
|
|
|
|
|
// Copy over all 3 channels
|
|
|
|
|
memcpy(d_data, tmp.data(), d_width * d_height * 3 * sizeof(T));
|
|
|
|
|
|
|
|
|
|
// Fill in RGBA
|
|
|
|
|
for (size_t i = 0; i < d_width * d_height; i++)
|
|
|
|
|
channel(3)[i] = std::numeric_limits<T>::max();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
template <typename T>
|
|
|
|
|
Image<uint8_t> Image<T>::to8bits()
|
2021-06-15 22:47:00 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
if (d_depth == 8)
|
2021-06-15 22:47:00 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
return *((image::Image<uint8_t> *)this);
|
|
|
|
|
}
|
|
|
|
|
else if (d_depth == 16)
|
|
|
|
|
{
|
|
|
|
|
image::Image<uint8_t> image8(d_width, d_height, d_channels);
|
|
|
|
|
for (size_t i = 0; i < data_size; i++)
|
|
|
|
|
image8[i] = d_data[i] >> 8;
|
|
|
|
|
return image8;
|
2021-06-15 22:47:00 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
return Image<uint8_t>(); // This should never happen
|
2021-06-15 22:47:00 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-06 15:33:44 +02:00
|
|
|
template <typename T>
|
2021-12-22 21:00:11 +01:00
|
|
|
Image<uint16_t> Image<T>::to16bits()
|
2021-06-15 22:47:00 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
if (d_depth == 16)
|
2021-06-15 22:47:00 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
return *((image::Image<uint16_t> *)this);
|
2021-06-15 22:47:00 +02:00
|
|
|
}
|
2021-12-22 21:00:11 +01:00
|
|
|
else if (d_depth == 8)
|
2021-07-16 20:43:46 +02:00
|
|
|
{
|
2021-12-22 21:00:11 +01:00
|
|
|
image::Image<uint16_t> image16(d_width, d_height, d_channels);
|
|
|
|
|
for (size_t i = 0; i < data_size; i++)
|
|
|
|
|
image16[i] = d_data[i] << 8;
|
|
|
|
|
return image16;
|
2021-07-16 20:43:46 +02:00
|
|
|
}
|
2021-08-06 15:33:44 +02:00
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
return Image<uint16_t>(); // This should never happen
|
2021-10-18 17:05:53 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-19 22:20:30 +02:00
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::load_img(std::string file)
|
|
|
|
|
{
|
|
|
|
|
std::ifstream file_sigature_src(file, std::ios::binary);
|
|
|
|
|
uint8_t signature[10];
|
|
|
|
|
file_sigature_src.read((char *)signature, 10);
|
|
|
|
|
if (signature[0] == 0xFF && signature[1] == 0xD8)
|
|
|
|
|
load_jpeg(file);
|
2022-09-19 23:26:46 +02:00
|
|
|
else if (signature[0] == 0x89 && signature[1] == 0x50 && signature[2] == 0x4E && signature[3] == 0x47)
|
2022-09-19 22:20:30 +02:00
|
|
|
load_png(file);
|
2023-08-06 01:26:41 +02:00
|
|
|
else if (signature[0] == 0xff && signature[1] == 0x4f && signature[2] == 0xff && signature[3] == 0x51)
|
|
|
|
|
load_j2k(file);
|
2023-10-13 23:20:16 -04:00
|
|
|
else if (signature[0] == 'P' && (signature[1] == '5' || signature[1] == '6'))
|
2023-10-14 01:01:28 +02:00
|
|
|
load_pbm(file);
|
2022-09-19 22:20:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::load_img(uint8_t *buffer, int size)
|
|
|
|
|
{
|
|
|
|
|
if (buffer[0] == 0xFF && buffer[1] == 0xD8)
|
|
|
|
|
load_jpeg(buffer, size);
|
2022-09-19 23:32:04 +02:00
|
|
|
else if (buffer[0] == 0x89 && buffer[1] == 0x50 && buffer[2] == 0x4E && buffer[3] == 0x47)
|
2022-09-19 22:20:30 +02:00
|
|
|
load_png(buffer, size);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-22 20:13:15 +02:00
|
|
|
template <typename T>
|
2023-08-02 23:03:18 -04:00
|
|
|
void Image<T>::save_img(std::string file, bool fast)
|
2022-09-22 20:13:15 +02:00
|
|
|
{
|
2023-08-08 19:40:38 +02:00
|
|
|
if (!append_ext(&file))
|
|
|
|
|
return;
|
2023-08-02 23:03:18 -04:00
|
|
|
logger->info("Saving " + file + "...");
|
2022-09-22 20:13:15 +02:00
|
|
|
if (file.find(".png") != std::string::npos)
|
2023-08-02 23:03:18 -04:00
|
|
|
save_png(file, fast);
|
2022-09-22 20:13:15 +02:00
|
|
|
else if (file.find(".jpeg") != std::string::npos || file.find(".jpg") != std::string::npos)
|
|
|
|
|
save_jpeg(file);
|
2023-08-06 01:26:41 +02:00
|
|
|
else if (file.find(".j2k") != std::string::npos)
|
|
|
|
|
save_j2k(file);
|
2023-10-14 01:01:28 +02:00
|
|
|
else if ((file.find(".ppm") != std::string::npos) || (file.find(".pgm") != std::string::npos) || (file.find(".pbm") != std::string::npos))
|
|
|
|
|
save_pbm(file);
|
2022-09-22 20:13:15 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-02 23:03:18 -04:00
|
|
|
// Append selected file extension
|
|
|
|
|
template <typename T>
|
2023-08-08 19:40:38 +02:00
|
|
|
bool Image<T>::append_ext(std::string *file)
|
2023-08-02 23:03:18 -04:00
|
|
|
{
|
2023-08-08 19:40:38 +02:00
|
|
|
// Do nothing if there's already an extension
|
2023-08-02 23:03:18 -04:00
|
|
|
if (file->find(".png") != std::string::npos ||
|
|
|
|
|
file->find(".jpeg") != std::string::npos ||
|
2023-08-08 19:40:38 +02:00
|
|
|
file->find(".jpg") != std::string::npos ||
|
2023-10-13 20:45:13 -04:00
|
|
|
file->find(".j2k") != std::string::npos ||
|
|
|
|
|
file->find(".pgm") != std::string::npos ||
|
|
|
|
|
file->find(".pbm") != std::string::npos ||
|
|
|
|
|
file->find(".ppm") != std::string::npos)
|
2023-08-02 23:03:18 -04:00
|
|
|
return true;
|
|
|
|
|
|
2023-08-08 19:40:38 +02:00
|
|
|
// Otherwise, load the user setting
|
2023-08-02 23:03:18 -04:00
|
|
|
std::string image_format;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
image_format = satdump::config::main_cfg["satdump_general"]["image_format"]["value"];
|
|
|
|
|
}
|
2023-08-08 19:40:38 +02:00
|
|
|
catch (std::exception &e)
|
2023-08-02 23:03:18 -04:00
|
|
|
{
|
|
|
|
|
logger->error("Image format not specified, and default format cannot be found! %s", e.what());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-13 20:45:13 -04:00
|
|
|
if (image_format != "png" && image_format != "jpg" && image_format != "j2k" && image_format != "pbm")
|
2023-08-02 23:03:18 -04:00
|
|
|
{
|
|
|
|
|
logger->error("Image format not specified, and default format is invalid!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*file += "." + image_format;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-22 21:00:11 +01:00
|
|
|
// Generate Images for uint16_t and uint8_t
|
|
|
|
|
template class Image<uint8_t>;
|
|
|
|
|
template class Image<uint16_t>;
|
2023-08-03 22:11:46 -04:00
|
|
|
}
|