diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarImage.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarImage.hpp')
-rw-r--r-- | source/core/StarImage.hpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/source/core/StarImage.hpp b/source/core/StarImage.hpp new file mode 100644 index 0000000..cce0d2f --- /dev/null +++ b/source/core/StarImage.hpp @@ -0,0 +1,313 @@ +#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 |