314 lines
8.3 KiB
C++
314 lines
8.3 KiB
C++
|
#ifndef STAR_IMAGE_HPP
|
||
|
#define STAR_IMAGE_HPP
|
||
|
|
||
|
#include "StarString.hpp"
|
||
|
#include "StarVector.hpp"
|
||
|
#include "StarIODevice.hpp"
|
||
|
|
||
|
namespace Star {
|
||
|
|
||
|
enum class PixelFormat {
|
||
|
RGB24,
|
||
|
RGBA32,
|
||
|
BGR24,
|
||
|
BGRA32
|
||
|
};
|
||
|
|
||
|
uint8_t bitsPerPixel(PixelFormat pf);
|
||
|
uint8_t bytesPerPixel(PixelFormat pf);
|
||
|
|
||
|
STAR_EXCEPTION(ImageException, StarException);
|
||
|
|
||
|
STAR_CLASS(Image);
|
||
|
|
||
|
// Holds an image of PixelFormat in row major order, with no padding, with (0,
|
||
|
// 0) defined to be the *lower left* corner.
|
||
|
class Image {
|
||
|
public:
|
||
|
static Image readPng(IODevicePtr device);
|
||
|
// Returns the size and pixel format that would be constructed from the given
|
||
|
// png file, without actually loading it.
|
||
|
static tuple<Vec2U, PixelFormat> readPngMetadata(IODevicePtr device);
|
||
|
|
||
|
static Image filled(Vec2U size, Vec4B color, PixelFormat pf = PixelFormat::RGBA32);
|
||
|
|
||
|
// Creates a zero size image
|
||
|
Image(PixelFormat pf = PixelFormat::RGBA32);
|
||
|
Image(Vec2U size, PixelFormat pf = PixelFormat::RGBA32);
|
||
|
Image(unsigned width, unsigned height, PixelFormat pf = PixelFormat::RGBA32);
|
||
|
~Image();
|
||
|
|
||
|
Image(Image const& image);
|
||
|
Image(Image&& image);
|
||
|
|
||
|
Image& operator=(Image const& image);
|
||
|
Image& operator=(Image&& image);
|
||
|
|
||
|
uint8_t bitsPerPixel() const;
|
||
|
uint8_t bytesPerPixel() const;
|
||
|
|
||
|
unsigned width() const;
|
||
|
unsigned height() const;
|
||
|
Vec2U size() const;
|
||
|
// width or height is 0
|
||
|
bool empty() const;
|
||
|
|
||
|
PixelFormat pixelFormat() const;
|
||
|
|
||
|
// If the image is empty, the data ptr will be null
|
||
|
uint8_t const* data() const;
|
||
|
uint8_t* data();
|
||
|
|
||
|
// Reallocate the image with the given width, height, and pixel format. The
|
||
|
// contents of the image are always zeroed after a call to reset.
|
||
|
void reset(Vec2U size, Maybe<PixelFormat> pf = {});
|
||
|
void reset(unsigned width, unsigned height, Maybe<PixelFormat> pf = {});
|
||
|
|
||
|
// Fill the image with a given color
|
||
|
void fill(Vec3B const& c);
|
||
|
void fill(Vec4B const& c);
|
||
|
|
||
|
// Fill a rectangle with a given color
|
||
|
void fillRect(Vec2U const& pos, Vec2U const& size, Vec3B const& c);
|
||
|
void fillRect(Vec2U const& pos, Vec2U const& size, Vec4B const& c);
|
||
|
|
||
|
// Color parameters / return values here are in whatever the internal format
|
||
|
// is. Fourth byte, if missing or not provided, is assumed to be 255. If
|
||
|
// the position is out of range, then throws an exception.
|
||
|
void set(Vec2U const& pos, Vec4B const& c);
|
||
|
void set(Vec2U const& pos, Vec3B const& c);
|
||
|
Vec4B get(Vec2U const& pos) const;
|
||
|
|
||
|
// Same as set / get, except color parameters / return values here are always
|
||
|
// RGB[A], and converts if necessary.
|
||
|
void setrgb(Vec2U const& pos, Vec4B const& c);
|
||
|
void setrgb(Vec2U const& pos, Vec3B const& c);
|
||
|
Vec4B getrgb(Vec2U const& pos) const;
|
||
|
|
||
|
// Get pixel value, but if pos is out of the normal pixel range, it is
|
||
|
// clamped back into the valid pixel range. Returns (0, 0, 0, 0) if image is
|
||
|
// empty.
|
||
|
Vec4B clamp(Vec2I const& pos) const;
|
||
|
Vec4B clamprgb(Vec2I const& pos) const;
|
||
|
|
||
|
// x / y versions of set / get, for compatibility
|
||
|
void set(unsigned x, unsigned y, Vec4B const& c);
|
||
|
void set(unsigned x, unsigned y, Vec3B const& c);
|
||
|
Vec4B get(unsigned x, unsigned y) const;
|
||
|
void setrgb(unsigned x, unsigned y, Vec4B const& c);
|
||
|
void setrgb(unsigned x, unsigned y, Vec3B const& c);
|
||
|
Vec4B getrgb(unsigned x, unsigned y) const;
|
||
|
Vec4B clamp(int x, int y) const;
|
||
|
Vec4B clamprgb(int x, int y) const;
|
||
|
|
||
|
// Must be 32 bitsPerPixel, no format conversion or bounds checking takes
|
||
|
// place. Very fast inline versions.
|
||
|
void set32(Vec2U const& pos, Vec4B const& c);
|
||
|
void set32(unsigned x, unsigned y, Vec4B const& c);
|
||
|
Vec4B get32(unsigned x, unsigned y) const;
|
||
|
|
||
|
// Must be 24 bitsPerPixel, no format conversion or bounds checking takes
|
||
|
// place. Very fast inline versions.
|
||
|
void set24(Vec2U const& pos, Vec3B const& c);
|
||
|
void set24(unsigned x, unsigned y, Vec3B const& c);
|
||
|
Vec3B get24(unsigned x, unsigned y) const;
|
||
|
|
||
|
// Called as callback(unsigned x, unsigned y, Vec4B const& pixel)
|
||
|
template <typename CallbackType>
|
||
|
void forEachPixel(CallbackType&& callback) const;
|
||
|
|
||
|
// Called as callback(unsigned x, unsigned y, Vec4B& pixel)
|
||
|
template <typename CallbackType>
|
||
|
void forEachPixel(CallbackType&& callback);
|
||
|
|
||
|
// Pixel rectangle, lower left position and size of rectangle.
|
||
|
Image subImage(Vec2U const& pos, Vec2U const& size) const;
|
||
|
|
||
|
// Copy given image into this one at pos
|
||
|
void copyInto(Vec2U const& pos, Image const& image);
|
||
|
// Draw given image over this one at pos (with alpha composition)
|
||
|
void drawInto(Vec2U const& pos, Image const& image);
|
||
|
|
||
|
// Convert this image into the given pixel format
|
||
|
Image convert(PixelFormat pixelFormat) const;
|
||
|
|
||
|
void writePng(IODevicePtr device) const;
|
||
|
|
||
|
private:
|
||
|
uint8_t* m_data;
|
||
|
unsigned m_width;
|
||
|
unsigned m_height;
|
||
|
PixelFormat m_pixelFormat;
|
||
|
};
|
||
|
|
||
|
inline uint8_t bitsPerPixel(PixelFormat pf) {
|
||
|
switch (pf) {
|
||
|
case PixelFormat::RGB24:
|
||
|
return 24;
|
||
|
case PixelFormat::RGBA32:
|
||
|
return 32;
|
||
|
case PixelFormat::BGR24:
|
||
|
return 24;
|
||
|
default:
|
||
|
return 32;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline uint8_t bytesPerPixel(PixelFormat pf) {
|
||
|
switch (pf) {
|
||
|
case PixelFormat::RGB24:
|
||
|
return 3;
|
||
|
case PixelFormat::RGBA32:
|
||
|
return 4;
|
||
|
case PixelFormat::BGR24:
|
||
|
return 3;
|
||
|
default:
|
||
|
return 4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline uint8_t Image::bitsPerPixel() const {
|
||
|
return Star::bitsPerPixel(m_pixelFormat);
|
||
|
}
|
||
|
|
||
|
inline uint8_t Image::bytesPerPixel() const {
|
||
|
return Star::bytesPerPixel(m_pixelFormat);
|
||
|
}
|
||
|
|
||
|
inline unsigned Image::width() const {
|
||
|
return m_width;
|
||
|
}
|
||
|
|
||
|
inline unsigned Image::height() const {
|
||
|
return m_height;
|
||
|
}
|
||
|
|
||
|
inline bool Image::empty() const {
|
||
|
return m_width == 0 || m_height == 0;
|
||
|
}
|
||
|
|
||
|
inline Vec2U Image::size() const {
|
||
|
return {m_width, m_height};
|
||
|
}
|
||
|
|
||
|
inline PixelFormat Image::pixelFormat() const {
|
||
|
return m_pixelFormat;
|
||
|
}
|
||
|
|
||
|
inline const uint8_t* Image::data() const {
|
||
|
return m_data;
|
||
|
}
|
||
|
|
||
|
inline uint8_t* Image::data() {
|
||
|
return m_data;
|
||
|
}
|
||
|
|
||
|
inline void Image::set(unsigned x, unsigned y, Vec4B const& c) {
|
||
|
return set({x, y}, c);
|
||
|
}
|
||
|
|
||
|
inline void Image::set(unsigned x, unsigned y, Vec3B const& c) {
|
||
|
return set({x, y}, c);
|
||
|
}
|
||
|
|
||
|
inline Vec4B Image::get(unsigned x, unsigned y) const {
|
||
|
return get({x, y});
|
||
|
}
|
||
|
|
||
|
inline void Image::setrgb(unsigned x, unsigned y, Vec4B const& c) {
|
||
|
return setrgb({x, y}, c);
|
||
|
}
|
||
|
|
||
|
inline void Image::setrgb(unsigned x, unsigned y, Vec3B const& c) {
|
||
|
return setrgb({x, y}, c);
|
||
|
}
|
||
|
|
||
|
inline Vec4B Image::getrgb(unsigned x, unsigned y) const {
|
||
|
return getrgb({x, y});
|
||
|
}
|
||
|
|
||
|
inline Vec4B Image::clamp(int x, int y) const {
|
||
|
return clamp({x, y});
|
||
|
}
|
||
|
|
||
|
inline Vec4B Image::clamprgb(int x, int y) const {
|
||
|
return clamprgb({x, y});
|
||
|
}
|
||
|
|
||
|
inline void Image::set32(Vec2U const& pos, Vec4B const& c) {
|
||
|
set32(pos[0], pos[1], c);
|
||
|
}
|
||
|
|
||
|
inline void Image::set32(unsigned x, unsigned y, Vec4B const& c) {
|
||
|
starAssert(m_data && x < m_width && y < m_height);
|
||
|
starAssert(bytesPerPixel() == 4);
|
||
|
|
||
|
size_t offset = y * m_width * 4 + x * 4;
|
||
|
m_data[offset] = c[0];
|
||
|
m_data[offset + 1] = c[1];
|
||
|
m_data[offset + 2] = c[2];
|
||
|
m_data[offset + 3] = c[3];
|
||
|
}
|
||
|
|
||
|
inline Vec4B Image::get32(unsigned x, unsigned y) const {
|
||
|
starAssert(m_data && x < m_width && y < m_height);
|
||
|
starAssert(bytesPerPixel() == 4);
|
||
|
|
||
|
Vec4B c;
|
||
|
size_t offset = y * m_width * 4 + x * 4;
|
||
|
c[0] = m_data[offset];
|
||
|
c[1] = m_data[offset + 1];
|
||
|
c[2] = m_data[offset + 2];
|
||
|
c[3] = m_data[offset + 3];
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
inline void Image::set24(Vec2U const& pos, Vec3B const& c) {
|
||
|
set24(pos[0], pos[1], c);
|
||
|
}
|
||
|
|
||
|
inline void Image::set24(unsigned x, unsigned y, Vec3B const& c) {
|
||
|
starAssert(m_data && x < m_width && y < m_height);
|
||
|
starAssert(bytesPerPixel() == 3);
|
||
|
|
||
|
size_t offset = y * m_width * 3 + x * 3;
|
||
|
m_data[offset] = c[0];
|
||
|
m_data[offset + 1] = c[1];
|
||
|
m_data[offset + 2] = c[2];
|
||
|
}
|
||
|
|
||
|
inline Vec3B Image::get24(unsigned x, unsigned y) const {
|
||
|
starAssert(m_data && x < m_width && y < m_height);
|
||
|
starAssert(bytesPerPixel() == 3);
|
||
|
|
||
|
Vec3B c;
|
||
|
size_t offset = y * m_width * 3 + x * 3;
|
||
|
c[0] = m_data[offset];
|
||
|
c[1] = m_data[offset + 1];
|
||
|
c[2] = m_data[offset + 2];
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
template <typename CallbackType>
|
||
|
void Image::forEachPixel(CallbackType&& callback) const {
|
||
|
for (unsigned y = 0; y < m_height; y++) {
|
||
|
for (unsigned x = 0; x < m_width; x++)
|
||
|
callback(x, y, get(x, y));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename CallbackType>
|
||
|
void Image::forEachPixel(CallbackType&& callback) {
|
||
|
for (unsigned y = 0; y < m_height; y++) {
|
||
|
for (unsigned x = 0; x < m_width; x++) {
|
||
|
Vec4B pixel = get(x, y);
|
||
|
callback(x, y, pixel);
|
||
|
set(x, y, pixel);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#endif
|