2021-12-22 21:00:11 +01:00
|
|
|
#include "image.h"
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include "resources.h"
|
|
|
|
|
|
|
|
|
|
namespace image
|
|
|
|
|
{
|
|
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::draw_pixel(int x, int y, T color[])
|
|
|
|
|
{
|
|
|
|
|
// Check we're not out of bounds
|
2022-05-12 18:19:01 +02:00
|
|
|
if (x < 0 || y < 0 || x >= (int)d_width || y >= (int)d_height)
|
2021-12-22 21:00:11 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < d_channels; c++)
|
|
|
|
|
channel(c)[y * d_width + x] = color[c];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::draw_line(int x0, int y0, int x1, int y1, T color[])
|
|
|
|
|
{
|
|
|
|
|
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
|
|
|
|
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
|
|
|
|
int err = (dx > dy ? dx : -dy) / 2, e2;
|
|
|
|
|
|
|
|
|
|
while (!(x0 == x1 && y0 == y1))
|
|
|
|
|
{
|
|
|
|
|
draw_pixel(x0, y0, color);
|
|
|
|
|
|
|
|
|
|
e2 = err;
|
|
|
|
|
|
|
|
|
|
if (e2 > -dx)
|
|
|
|
|
{
|
|
|
|
|
err -= dy;
|
|
|
|
|
x0 += sx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e2 < dy)
|
|
|
|
|
{
|
|
|
|
|
err += dx;
|
|
|
|
|
y0 += sy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::draw_circle(int x0, int y0, int radius, T color[], bool fill)
|
|
|
|
|
{
|
|
|
|
|
if (fill) // Filled circle
|
|
|
|
|
{
|
|
|
|
|
int x = radius;
|
|
|
|
|
int y = 0;
|
|
|
|
|
int err = 1 - x;
|
|
|
|
|
|
|
|
|
|
while (x >= y)
|
|
|
|
|
{
|
|
|
|
|
draw_line(-x + x0, y + y0, x + x0, y + y0, color);
|
|
|
|
|
if (y != 0)
|
|
|
|
|
draw_line(-x + x0, -y + y0, x + x0, -y + y0, color);
|
|
|
|
|
|
|
|
|
|
y++;
|
|
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
|
err += 2 * y + 1;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (x >= y)
|
|
|
|
|
{
|
|
|
|
|
draw_line(-y + 1 + x0, x + y0, y - 1 + x0, x + y0, color);
|
|
|
|
|
draw_line(-y + 1 + x0, -x + y0, y - 1 + x0, -x + y0, color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x--;
|
|
|
|
|
err += 2 * (y - x + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Hollow circle
|
|
|
|
|
{
|
|
|
|
|
int err = 1 - radius;
|
|
|
|
|
int xx = 0;
|
|
|
|
|
int yy = -2 * radius;
|
|
|
|
|
int x = 0;
|
|
|
|
|
int y = radius;
|
|
|
|
|
|
|
|
|
|
draw_pixel(x0, y0 + radius, color);
|
|
|
|
|
draw_pixel(x0, y0 - radius, color);
|
|
|
|
|
draw_pixel(x0 + radius, y0, color);
|
|
|
|
|
draw_pixel(x0 - radius, y0, color);
|
|
|
|
|
|
|
|
|
|
while (x < y)
|
|
|
|
|
{
|
|
|
|
|
if (err >= 0)
|
|
|
|
|
{
|
|
|
|
|
y--;
|
|
|
|
|
yy += 2;
|
|
|
|
|
err += yy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x++;
|
|
|
|
|
xx += 2;
|
|
|
|
|
err += xx + 1;
|
|
|
|
|
|
|
|
|
|
draw_pixel(x0 + x, y0 + y, color);
|
|
|
|
|
draw_pixel(x0 - x, y0 + y, color);
|
|
|
|
|
draw_pixel(x0 + x, y0 - y, color);
|
|
|
|
|
draw_pixel(x0 - x, y0 - y, color);
|
|
|
|
|
draw_pixel(x0 + y, y0 + x, color);
|
|
|
|
|
draw_pixel(x0 - y, y0 + x, color);
|
|
|
|
|
draw_pixel(x0 + y, y0 - x, color);
|
|
|
|
|
draw_pixel(x0 - y, y0 - x, color);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::draw_image(int c, Image<T> image, int x0, int y0)
|
|
|
|
|
{
|
|
|
|
|
// Get min height and width, mostly for safety
|
2021-12-27 19:18:02 +01:00
|
|
|
int width = std::min<int>(d_width, x0 + image.width()) - x0;
|
|
|
|
|
int height = std::min<int>(d_height, y0 + image.height()) - y0;
|
2021-12-22 21:00:11 +01:00
|
|
|
|
|
|
|
|
for (int x = 0; x < width; x++)
|
|
|
|
|
for (int y = 0; y < height; y++)
|
2022-02-01 14:04:05 +01:00
|
|
|
if (y + y0 >= 0 && x + x0 >= 0)
|
|
|
|
|
channel(c)[(y + y0) * d_width + x + x0] = image[y * image.width() + x];
|
2021-12-22 21:00:11 +01:00
|
|
|
|
|
|
|
|
if (c == 0 && image.channels() == d_channels) // Special case for non-grayscale images
|
|
|
|
|
{
|
|
|
|
|
for (int c = 1; c < d_channels; c++)
|
|
|
|
|
for (int x = 0; x < width; x++)
|
|
|
|
|
for (int y = 0; y < height; y++)
|
2022-02-01 14:04:05 +01:00
|
|
|
if (y + y0 >= 0 && x + x0 >= 0)
|
|
|
|
|
channel(c)[(y + y0) * d_width + x + x0] = image.channel(c)[y * image.width() + x];
|
2021-12-22 21:00:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 16:37:38 +01:00
|
|
|
std::vector<Image<uint8_t>> make_font(int size, bool text_mode)
|
2021-12-22 21:00:11 +01:00
|
|
|
{
|
|
|
|
|
Image<uint8_t> fontFile;
|
2022-01-27 16:37:38 +01:00
|
|
|
fontFile.load_png(resources::getResourcePath("fonts/Roboto-Regular.png"));
|
2021-12-22 21:00:11 +01:00
|
|
|
|
|
|
|
|
std::vector<Image<uint8_t>> font;
|
|
|
|
|
|
2022-01-27 16:37:38 +01:00
|
|
|
int char_size = 120;
|
2021-12-22 21:00:11 +01:00
|
|
|
int char_count = 95;
|
|
|
|
|
int char_edge_crop = 15;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < char_count; i++)
|
|
|
|
|
{
|
|
|
|
|
Image<uint8_t> char_img = fontFile;
|
|
|
|
|
char_img.crop(i * char_size, 0, (i + 1) * char_size, char_size);
|
|
|
|
|
char_img.crop(char_edge_crop, char_img.width() - char_edge_crop);
|
2022-01-27 16:37:38 +01:00
|
|
|
char_img.resize_bilinear((float)char_img.width() * ((float)size / (float)char_size), size, text_mode);
|
2021-12-22 21:00:11 +01:00
|
|
|
font.push_back(char_img);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return font;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
void Image<T>::draw_text(int x0, int y0, T color[], std::vector<Image<uint8_t>> font, std::string text)
|
|
|
|
|
{
|
|
|
|
|
int pos = x0;
|
|
|
|
|
T *colorf = new T[d_channels];
|
|
|
|
|
|
|
|
|
|
for (char character : text)
|
|
|
|
|
{
|
2022-01-27 16:37:38 +01:00
|
|
|
if (size_t(character - 31) > font.size())
|
2021-12-22 21:00:11 +01:00
|
|
|
continue;
|
|
|
|
|
|
2022-01-27 16:37:38 +01:00
|
|
|
Image<uint8_t> &img = font[character - 31];
|
2021-12-22 21:00:11 +01:00
|
|
|
|
2022-05-12 18:19:01 +02:00
|
|
|
for (int x = 0; x < (int)img.width(); x++)
|
2021-12-22 21:00:11 +01:00
|
|
|
{
|
2022-05-12 18:19:01 +02:00
|
|
|
for (int y = 0; y < (int)img.height(); y++)
|
2021-12-22 21:00:11 +01:00
|
|
|
{
|
|
|
|
|
float value = img[y * img.width() + x] / 255.0f;
|
|
|
|
|
|
|
|
|
|
if (value == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < d_channels; c++)
|
|
|
|
|
colorf[c] = color[c] * value;
|
|
|
|
|
|
|
|
|
|
draw_pixel(pos + x, y0 + y, colorf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pos += img.width();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete[] colorf;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 12:12:16 +01:00
|
|
|
template <typename T>
|
2022-05-12 18:19:01 +02:00
|
|
|
Image<T> generate_text_image(std::string text, T color[], int height, int padX, int padY)
|
|
|
|
|
{
|
|
|
|
|
std::vector<Image<uint8_t>> font = make_font(height - 2 * padY, false);
|
|
|
|
|
int width = font[0].width() * text.length() + 2 * padX;
|
2022-01-27 16:37:38 +01:00
|
|
|
Image<T> out(width, height, 1);
|
|
|
|
|
out.fill(0);
|
|
|
|
|
out.draw_text(padX, 0, color, font, text);
|
2022-01-27 12:12:16 +01:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-27 16:37:38 +01:00
|
|
|
template Image<uint8_t> generate_text_image(std::string text, uint8_t color[], int height, int padX, int padY);
|
|
|
|
|
template Image<uint16_t> generate_text_image(std::string text, uint16_t color[], int height, int padX, int padY);
|
|
|
|
|
|
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>;
|
2022-01-27 16:37:38 +01:00
|
|
|
}
|