imagebuffer.h File Reference

Go to the documentation of this file. Source: include/ffw/media/imagebuffer.h

/* This file is part of FineFramework project */
#ifndef FFW_MEDIA_IMAGE_BUFFER
#define FFW_MEDIA_IMAGE_BUFFER

#include "config.h"
#include <cstring>
#include <algorithm>
#include <memory>
#include <cmath>

namespace ffw {
    enum class ImageType {
        INVALID = 0,
        BITMAP_1,
        GRAYSCALE_4,
        GRAYSCALE_ALPHA_4,
        GRAYSCALE_8,
        GRAYSCALE_ALPHA_8,
        GRAYSCALE_16,
        GRAYSCALE_16F,
        GRAYSCALE_ALPHA_16,
        GRAYSCALE_ALPHA_16F,
        GRAYSCALE_32F,
        GRAYSCALE_ALPHA_32F,
        RG_88,
        RG_1616,
        RG_1616F,
        RG_3232F,
        RGB_565,
        RGB_888,
        RGB_161616,
        RGB_161616F,
        RGB_323232F,
        RGB_ALPHA_5551,
        RGB_ALPHA_4444,
        RGB_ALPHA_8888,
        RGB_ALPHA_16161616,
        RGB_ALPHA_16161616F,
        RGB_ALPHA_32323232F,
        RGB_DXT1,
        RGB_ALPHA_DXT3,
        RGB_ALPHA_DXT5,
        RED_RGTC1,
        RED_GREEN_RGTC2,
        SIGNED_RED_RGTC1,
        SIGNED_RED_GREEN_RGTC2,
        CUSTOM,
    };
    class ImageFormat {
    public:
        inline ImageFormat():
            width(0),height(0),depth(0),format(ffw::ImageType::INVALID){
        }
        inline ImageFormat(int width, int height, int depth, ffw::ImageType format):
            width(width),height(height),depth(depth),format(format){
        }
        inline ImageFormat(const ImageFormat& other):
            width(other.width),height(other.height),depth(other.depth),format(other.format){
        }
        inline ImageType getImageType() const {
            return format;
        }
        inline int getWidth(int mipmap = 0) const {
            if (mipmap <= 0)return width;
            return width >> mipmap;
        }
        inline int getHeight(int mipmap = 0) const {
            if (mipmap <= 0)return height;
            return height >> mipmap;
        }
        inline int getDepth(int mipmap = 0) const {
            if (mipmap <= 0)return depth;
            return depth >> mipmap;
        }
        /*
         * @brief Returns the number of bytes needed for one single row of pixels
         * @param mipmap Optional mipmap level (default is zero)
         */
        inline size_t getStrideSize(int mipmap = 0) const {
            if(isCompressed()) {
                return getMipMapSize(mipmap) / getHeight(mipmap);
            }
            return size_t(std::ceil(getWidth(mipmap) * (getBitsPerPixel() / 8.0f)));
        }
        inline int getBitsPerPixel() const {
            switch (format) {
                case ImageType::BITMAP_1: return 1;
                case ImageType::GRAYSCALE_4: return 4;
                case ImageType::GRAYSCALE_ALPHA_4: return 8;
                case ImageType::GRAYSCALE_8: return 8;
                case ImageType::GRAYSCALE_ALPHA_8: return 16;
                case ImageType::GRAYSCALE_16: return 16;
                case ImageType::GRAYSCALE_16F: return 16;
                case ImageType::GRAYSCALE_ALPHA_16: return 32;
                case ImageType::GRAYSCALE_ALPHA_16F: return 32;
                case ImageType::GRAYSCALE_32F: return 32;
                case ImageType::GRAYSCALE_ALPHA_32F: return 64;
                case ImageType::RGB_565: return 16;
                case ImageType::RGB_888: return 24;
                case ImageType::RGB_161616: return 48;
                case ImageType::RGB_323232F: return 96;
                case ImageType::RGB_ALPHA_5551: return 16;
                case ImageType::RGB_ALPHA_4444: return 16;
                case ImageType::RGB_ALPHA_8888: return 32;
                case ImageType::RGB_ALPHA_16161616: return 64;
                case ImageType::RGB_ALPHA_16161616F: return 64;
                case ImageType::RGB_ALPHA_32323232F: return 128;
                case ImageType::RGB_DXT1: return 4;
                case ImageType::RGB_ALPHA_DXT3: return 8;
                case ImageType::RGB_ALPHA_DXT5: return 8;
                case ImageType::RED_RGTC1: return 4;
                case ImageType::SIGNED_RED_RGTC1: return 4;
                case ImageType::RED_GREEN_RGTC2: return 8;
                case ImageType::SIGNED_RED_GREEN_RGTC2: return 8;
                case ImageType::RG_88: return 16;
                case ImageType::RG_1616: return 32;
                case ImageType::RG_1616F: return 32;
                case ImageType::RG_3232F: return 48;
                default: return 0;
            }
        }
        inline int getBitDepth() const {
            switch (format) {
                case ImageType::BITMAP_1: return 1;
                case ImageType::GRAYSCALE_4: return 4;
                case ImageType::GRAYSCALE_ALPHA_4: return 4;
                case ImageType::GRAYSCALE_8: return 8;
                case ImageType::GRAYSCALE_ALPHA_8: return 8;
                case ImageType::GRAYSCALE_16: return 16;
                case ImageType::GRAYSCALE_16F: return 16;
                case ImageType::GRAYSCALE_ALPHA_16: return 16;
                case ImageType::GRAYSCALE_ALPHA_16F: return 16;
                case ImageType::GRAYSCALE_32F: return 32;
                case ImageType::GRAYSCALE_ALPHA_32F: return 32;
                case ImageType::RGB_565: return 5;
                case ImageType::RGB_888: return 8;
                case ImageType::RGB_161616: return 16;
                case ImageType::RGB_323232F: return 32;
                case ImageType::RGB_ALPHA_5551: return 5;
                case ImageType::RGB_ALPHA_4444: return 4;
                case ImageType::RGB_ALPHA_8888: return 8;
                case ImageType::RGB_ALPHA_16161616: return 16;
                case ImageType::RGB_ALPHA_16161616F: return 16;
                case ImageType::RGB_ALPHA_32323232F: return 32;
                case ImageType::RGB_DXT1: return 8;
                case ImageType::RGB_ALPHA_DXT3: return 8;
                case ImageType::RGB_ALPHA_DXT5: return 8;
                case ImageType::RED_RGTC1: return 8;
                case ImageType::SIGNED_RED_RGTC1: return 8;
                case ImageType::RED_GREEN_RGTC2: return 8;
                case ImageType::SIGNED_RED_GREEN_RGTC2: return 8;
                case ImageType::RG_88: return 8;
                case ImageType::RG_1616: return 16;
                case ImageType::RG_1616F: return 16;
                case ImageType::RG_3232F: return 32;
                default: return 0;
            }
        }
        inline int getNumberOfChannels() const {
            switch (format) {
                case ImageType::BITMAP_1: return 1;
                case ImageType::GRAYSCALE_4: return 1;
                case ImageType::GRAYSCALE_ALPHA_4: return 2;
                case ImageType::GRAYSCALE_8: return 1;
                case ImageType::GRAYSCALE_ALPHA_8: return 2;
                case ImageType::GRAYSCALE_16: return 1;
                case ImageType::GRAYSCALE_16F: return 1;
                case ImageType::GRAYSCALE_ALPHA_16: return 2;
                case ImageType::GRAYSCALE_ALPHA_16F: return 2;
                case ImageType::GRAYSCALE_32F: return 1;
                case ImageType::GRAYSCALE_ALPHA_32F: return 2;
                case ImageType::RGB_565: return 3;
                case ImageType::RGB_888: return 3;
                case ImageType::RGB_161616: return 3;
                case ImageType::RGB_323232F: return 3;
                case ImageType::RGB_ALPHA_5551: return 4;
                case ImageType::RGB_ALPHA_4444: return 4;
                case ImageType::RGB_ALPHA_8888: return 4;
                case ImageType::RGB_ALPHA_16161616: return 4;
                case ImageType::RGB_ALPHA_16161616F: return 4;
                case ImageType::RGB_ALPHA_32323232F: return 4;
                case ImageType::RGB_DXT1: return 3;
                case ImageType::RGB_ALPHA_DXT3: return 4;
                case ImageType::RGB_ALPHA_DXT5: return 4;
                case ImageType::RED_RGTC1: return 1;
                case ImageType::SIGNED_RED_RGTC1: return 1;
                case ImageType::RED_GREEN_RGTC2: return 2;
                case ImageType::SIGNED_RED_GREEN_RGTC2: return 2;
                case ImageType::RG_88: return 2;
                case ImageType::RG_1616: return 2;
                case ImageType::RG_1616F: return 2;
                case ImageType::RG_3232F: return 2;
                default: return 0;
            }
        }
        inline bool isCompressed() const {
            switch (format) {
                case ImageType::RGB_DXT1:
                case ImageType::RGB_ALPHA_DXT3:
                case ImageType::RGB_ALPHA_DXT5: 
                case ImageType::RED_RGTC1:
                case ImageType::SIGNED_RED_RGTC1:
                case ImageType::RED_GREEN_RGTC2:
                case ImageType::SIGNED_RED_GREEN_RGTC2:
                    return true;
                default: return false;
            }
        }
        inline size_t getMipMapSize(int level) const {
            int w = getWidth(level) == 0 ? 1 : getWidth(level);
            int h = getHeight(level) == 0 ? 1 : getHeight(level);
            int d = getDepth(level) == 0 ? 1 : getDepth(level);
            switch(format) {
                case ImageType::RED_RGTC1:
                case ImageType::SIGNED_RED_RGTC1:
                case ImageType::RGB_DXT1:
                    return ((w+3)/4) * ((h+3)/4) * d * 8;
                case ImageType::RED_GREEN_RGTC2:
                case ImageType::SIGNED_RED_GREEN_RGTC2:
                case ImageType::RGB_ALPHA_DXT3:
                case ImageType::RGB_ALPHA_DXT5:
                    return ((w+3)/4) * ((h+3)/4) * d * 16;
                default:
                    return getStrideSize(level) * h * d;
            }
        }
        inline size_t getMipMapPtrOffset(int level) const {
            size_t cpy = 0;
            for (int i = 0; i < level; i++) {
                cpy += getMipMapSize(i);
            }
            return cpy;
        }
        inline void swap(ImageFormat& other) {
            using std::swap;
            swap(width, other.width);
            swap(height, other.height);
            swap(depth, other.depth);
            swap(format, other.format);
        }
        inline ImageFormat& operator = (const ImageFormat& other) {
            width = other.width;
            height = other.height;
            depth = other.depth;
            format = other.format;
            return *this;
        }
        inline size_t getAllocationSize(int mipmaps) const {
            size_t size = 0;
            for (int i = 0; i < mipmaps; i++) {
                size += getMipMapSize(i);
            }
            return size;
        }
        inline bool operator==(const ImageFormat& other) const {
            return width == other.width &&
                height == other.height &&
                depth == other.depth &&
                format == other.format;
        }
    protected:
        ffw::ImageType format;
        int width;
        int height;
        int depth;
    };
    class ImageBuffer: public ImageFormat {
    public:
        inline ImageBuffer() {
            mipmaps = 0;
        }
        ImageBuffer(const ImageBuffer& other) = delete;
        inline ImageBuffer(ImageBuffer&& other):ptr(std::move(other.ptr)) {
            other.ptr.reset();
            width = other.width;
            height = other.height;
            depth = other.depth;
            format = other.format;
            mipmaps = other.mipmaps;
        }
        inline virtual ~ImageBuffer() {
            destroy();
        }
        inline bool isAllocated() const {
            return ptr != NULL;
        }
        inline operator bool() const {
            return isAllocated();
        }
        inline int getNumOfMipMaps() const {
            return mipmaps;
        }
        inline void swap(ImageBuffer& second) {
            ImageFormat::swap(second);
            std::swap(ptr, second.ptr);
            std::swap(mipmaps, second.mipmaps);
        }
        inline void destroy() {
            if (ptr != NULL) {
                ptr.reset();
            }
            format = ffw::ImageType::INVALID;
            width = 0;
            depth = 0;
            height = 0;
            mipmaps = 0;
        }
        inline bool allocate(int w, int h, ffw::ImageType type, const void* pixels = NULL, int mips = 0) {
            if (w <= 0 || h <= 0)return false;
            if (mips < 0)return false;
            if (type == ffw::ImageType::INVALID)return false;

            destroy();
            width = w;
            height = h;
            depth = 0;
            format = type;
            mipmaps = mips;
            ptr.reset(new unsigned char[getAllocationSize(mipmaps)]);
            if (pixels != NULL)memcpy(ptr.get(), pixels, getAllocationSize(mipmaps));
            return true;
        }
        inline unsigned char* getPtr() {
            return ptr.get();
        }
        inline const unsigned char* getPtr() const {
            return ptr.get();
        }
        inline unsigned char* getMipMapPtr(int level) {
            return ptr.get() + getMipMapPtrOffset(level);
        }
        inline const unsigned char* getMipMapPtr(int level) const {
            return ptr.get() + getMipMapPtrOffset(level);
        }
        inline size_t getSize() const {
            return height * getStrideSize();
        }
        ImageBuffer& operator = (const ImageBuffer& other) = delete;
        inline ImageBuffer& operator = (ImageBuffer&& other) {
            if (this != &other) {
                swap(other);
            }
            return *this;
        }
        inline bool operator==(const ImageBuffer& other) const {
            return ImageFormat::operator==(other) && mipmaps == other.mipmaps && ptr == other.ptr;
        }
    private:
        std::unique_ptr<unsigned char> ptr;
        int mipmaps;
    };
};

inline void swap(ffw::ImageBuffer& first, ffw::ImageBuffer& second) {
    first.swap(second);
}

#endif