satdump/src-core/image/image.h
2025-05-27 18:42:18 +01:00

426 lines
No EOL
15 KiB
C++

#pragma once
/**
* @file image.h
* @brief Core Image class
*/
#include <cstddef>
#include <cstdint>
#include <vector>
namespace satdump
{
namespace image
{
class Image
{
protected:
// Image buffer
size_t data_size = 0;
size_t type_size = 0;
void *d_data = nullptr;
// Size and parameters
int d_depth = 0;
int d_maxv = 0;
size_t d_width = 0;
size_t d_height = 0;
int d_channels = 0;
public:
// Metadata stuff
void *metadata_obj = nullptr; // DO NOT USE DIRECTLY!
protected:
void copy_meta(const Image &img);
public:
/**
* @brief Init null image, with no data buffer
*/
Image();
/**
* @brief Init emtpy image, set to 0 but with an allocated data buffer
* @param bit_depth depth of the image (usually 8 or 16)
* @param width Image width in pixels
* @param height Image height in pixels
* @param channels Number of channels. See init() for more info
*/
Image(int bit_depth, size_t width, size_t height, int channels);
/**
* @brief Init an image, reading the contents of the data buffer from
* a provided buffer. This is useful to for example turn a uint16_t buffer
* directly into an usable image.
* @param buffer pointer to read from
* @param bit_depth depth of the image (usually 8 or 16)
* @param width Image width in pixels
* @param height Image height in pixels
* @param channels Number of channels. See init() for more info
*/
Image(void *buffer, int bit_depth, size_t width, size_t height, int channels);
Image(const Image &img); // Copy constructor
~Image(); // Destructor
Image &operator=(const Image &img);
public:
/**
* @brief Convert this image from B&W to RGB (if it is B&W / RGBA)
*/
void to_rgb();
/**
* @brief Convert this image from to RGBA (if it is B&W / RGB)
*/
void to_rgba();
/**
* @brief Convert to 8-bits. Returns the current image if it's already 8-bits
*/
Image to8bits();
/**
* @brief Convert to 16-bits. Returns the current image if it's already 16-bits
*/
Image to16bits();
/**
* @brief Converts to a specific target bit depth
* @param bit_depth to convert to
* @return converted image
*/
Image to_depth(int bit_depth);
public:
/**
* @brief Draw a B&W Image onto an image channel, or if channel
* is kept at 0, RGB or RGBA images as well.
* @param channel index. If 0, draws as color image
* @param image the image to draw
* @param x offset
* @param y offset
*/
void draw_image(int channel, Image image, int x = 0, int y = 0);
/**
* @brief Draw an alpha Image onto an image or if channel
* is kept at 0, RGB or RGBA images as well.
* Respects the image's Alpha channel.
* @param channel index. If 0, draws as color image
* @param image the image to draw
* @param x offset
* @param y offset
*/
void draw_image_alpha(Image image, int x = 0, int y = 0);
/**
* @brief Set a pixel's color
* @param x position
* @param y position
* @param color of the pixel
*/
void draw_pixel(size_t x, size_t y, std::vector<double> color);
/**
* @brief Draw a line with Bresenham's algorithm
* @param x0 line start X position
* @param y0 line start Y position
* @param x1 line stop X position
* @param y1 line stop Y position
* @param color of the line
*/
void draw_line(int x0, int y0, int x1, int y1, std::vector<double> color);
/**
* @brief Draw a circle with Bresenham's Midpoint algorithm
* @param x0 center X position
* @param y0 center Y position
* @param radius in pixels
* @param color of the circle
* @param fill should be true if the circle is to be filled
*/
void draw_circle(int x0, int y0, int radius, std::vector<double> color, bool fill = false);
/**
* @brief Draw a rectangle onto the image
* @param x0 rectangle start X position
* @param y0 rectangle start Y position
* @param x1 rectangle stop X position
* @param y1 rectangle stop Y position
* @param color of the rectangle
* @param fill should be true if the rectangle is to be filled
*/
void draw_rectangle(int x0, int y0, int x1, int y1, std::vector<double> color, bool fill = true);
public:
/**
* @brief Init (empty, filled with zeroes) image buffer
* @param bit_depth Image bit depth. Usually should be 8 or 16
* @param width Image width in pixels
* @param height Image height in pixels
* @param channels Number of channels (1 = Grayscale, 2 = GA, 3 = RGB, 4 = RGBA)
*/
void init(int bit_depth, size_t width, size_t height, int channels);
/**
* @brief Delete current buffer (and set size to 0)
*/
void clear();
public:
/**
* @brief Clamp input value to what this image can handle
* @param input the input value
* @return clamped value (depends on bit depth)
*/
int clamp(int input);
/**
* @brief Clamp input value to what this image can handle as float (0, 1)
* @param input the input value
* @return value between 0 and 1
*/
double clampf(double input);
/**
* @brief Returns the RAW void pointer. MUST ONLY BE USED IF YOU KNOW WHAT YOU ARE DOING!
* @return the void pointer. Be. Careful.
*/
void *raw_data() const { return d_data; }
/**
* @brief Returns image bit depth
* @return bit depth
*/
int depth() const { return d_depth; }
/**
* @brief Returns image type size (in bytes)
* @return type size, bytes
*/
int typesize() const { return type_size; }
/**
* @brief Returns image width
* @return image width in pixels
*/
size_t width() const { return d_width; }
/**
* @brief Returns image height
* @return image height in pixels
*/
size_t height() const { return d_height; }
/**
* @brief Returns image channel count
* @return number of image channels
*/
int channels() const { return d_channels; }
/**
* @brief Returns image data size
* @return Image data size, in pixels
*/
size_t size() const { return data_size; }
/**
* @brief Returns image max value
* @return maximum int value of an image pixels
*/
int maxval() const { return d_maxv; }
public:
/**
* @brief Crop an image region. Must be with x0 <= x1 and y0 <= y1
* @param x0 x position of the first pixel
* @param y0 y position of the first pixel
* @param x1 x position of the second pixel
* @param y1 y position of the second pixel
*/
void crop(int x0, int y0, int x1, int y1);
/**
* @brief Crop an image region, x axis only
* @param x0 x position of the first column
* @param x1 x position of the second column
*/
void crop(int x0, int x1);
/**
* @brief Crop an image region. Must be with x0 <= x1 and y0 <= y1.
* Provides image as a return value
* @param x0 x position of the first pixel
* @param y0 y position of the first pixel
* @param x1 x position of the second pixel
* @param y1 y position of the second pixel
* @return Cropped image
*/
Image crop_to(int x0, int y0, int x1, int y1);
/**
* @brief Crop an image region, x asus only
* Provides image as a return value
* @param x0 x position of the first column
* @param x1 x position of the second column
* @return Cropped image
*/
Image crop_to(int x0, int x1);
public:
/**
* @brief Mirror the image
* @param x mirror on X axis
* @param y mirror on Y axis
*/
void mirror(bool x, bool y);
public:
/**
* @brief Resize image, using a simple pixel scaling attribution
* (not the best, but fast)
* @param width x size to resize to
* @param height y size to resize to
*/
void resize(int width, int height);
/**
* @brief Resize image, to another image. Same as resize otherwise
* @param width x size to resize to
* @param height y size to resize to
* @return output image
*/
Image resize_to(int width, int height);
/**
* @brief Resize image, using a bilinear algorithm
* @param width x size to resize to
* @param height y size to resize to
* @param text_mode makes any pixel that is no 0 the maximum value of the pixel
*/
void resize_bilinear(int width, int height, bool text_mode = false);
/**
* @brief Get a pixel from the image, but with the option to interpolate
* fractional pixels.
* @param channel index of the channel to use
* @param x position
* @param y position
*/
int get_pixel_bilinear(int channel, double x, double y);
public:
/**
* @brief Fill image with a single value
* @param val the value
*/
void fill(int val);
/**
* @brief Fill image with a color
* @param color the color, must match the number of image channels
*/
void fill_color(std::vector<double> color);
public:
/**
* @brief Standard int set. No bound check!
* Variants to pass a channel index and X / Y position
* are also available.
* @param p index of the pixel to set
* @param v value to set
*/
void set(size_t p, int v)
{
if (d_depth > 8)
((uint16_t *)d_data)[p] = v;
else
((uint8_t *)d_data)[p] = v;
}
/**
* @brief Standard int get. No bound check!
* Variants to pass a channel index and X / Y position
* are also available.
* @param p index of the pixel to get
* @return pixel value
*/
int get(size_t p)
{
if (d_depth > 8)
return ((uint16_t *)d_data)[p];
else
return ((uint8_t *)d_data)[p];
}
/**
* @brief Standard float set. No bound check!
* Variants to pass a channel index and X / Y position
* are also available.
* @param p index of the pixel to set
* @param v value to set between 0 and 1
*/
void setf(size_t p, double v)
{
if (d_depth > 8)
((uint16_t *)d_data)[p] = v * (double)d_maxv;
else
((uint8_t *)d_data)[p] = v * (double)d_maxv;
}
/**
* @brief Standard float get. No bound check!
* Variants to pass a channel index and X / Y position
* are also available.
* @param p index of the pixel to get
* @return pixel value between 0 and 1
*/
double getf(size_t p)
{
if (d_depth > 8)
return (double)((uint16_t *)d_data)[p] / (double)d_maxv;
else
return (double)((uint8_t *)d_data)[p] / (double)d_maxv;
}
// Easier int set/get
void set(size_t channel, size_t p, int v) { set(channel * d_width * d_height + p, v); }
int get(size_t channel, size_t p) { return get(channel * d_width * d_height + p); }
void set(size_t channel, size_t x, size_t y, int v) { set(channel * d_width * d_height + y * d_width + x, v); }
int get(size_t channel, size_t x, size_t y) { return get(channel * d_width * d_height + y * d_width + x); }
// Easier double setf/getf
void setf(size_t channel, size_t p, double v) { setf(channel * d_width * d_height + p, v); }
double getf(size_t channel, size_t p) { return getf(channel * d_width * d_height + p); }
void setf(size_t channel, size_t x, size_t y, double v) { setf(channel * d_width * d_height + y * d_width + x, v); }
double getf(size_t channel, size_t x, size_t y) { return getf(channel * d_width * d_height + y * d_width + x); }
};
/**
* @brief Copy part of an image over. Safe way to do it with variable bit
* depths & channels
* @param img1 image to copy to
* @param pos1 position to copy to
* @param img2 image to copy from
* @param pos2 position to copy from
* @param px_size number of pixels to copy
*/
void imemcpy(Image &img1, size_t pos1, Image &img2, size_t pos2, size_t px_size);
/**
* @brief Convert an image to a 8-bit RGBA buffer for OpenGL/etc textures
* @param img image to convert
* @param output output buffer
*/
void image_to_rgba(Image &img, uint32_t *output);
} // namespace image
} // namespace satdump
// TODOREWORK!!!!! REMOVE. Temporary namespace hack until everything is moved over!
namespace image
{
using namespace satdump::image;
} // namespace image