| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
-
- // zlib open source license
- //
- // Copyright (c) 2019 to 2025 David Forsgren Piuva
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- //
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- #ifndef DFPSR_IMAGE_TYPES
- #define DFPSR_IMAGE_TYPES
- #include "PackOrder.h"
- #include "../../math/IRect.h"
- #include "../../api/bufferAPI.h"
- namespace dsr {
- enum class ImageFileFormat {
- Unknown, // Used as an error code for unidentified formats.
- JPG, // Lossy compressed image format storing brightness separated from red and blue offsets using the discrete cosine transform of each block.
- PNG, // Lossless compressed image format. Some image editors don't save RGB values where alpha is zero, which will bleed through black edges in bi-linear interpolation when the interpolated alpha is not zero.
- TGA, // Lossless compressed format. Applications usually give Targa better control over the alpha channel than PNG, but it's more common that the Targa specification is interpreted in incompatible ways.
- BMP // Uncompressed image format for storing data that does not really represent an image and you just want it to be exact.
- };
- // Packed into 2 bits in ImageDimensions.
- enum class PixelFormat : uint32_t {
- MonoU8, // Gray-scale image of 8 bits per pixel (0..255).
- MonoU16,
- MonoF32,
- RgbaU8 // RGBA colors in any order. 8 bits per channel (0..255). 32 bits per pixel.
- };
- // Start offset and stride is stored in pixels and the getters in imageAPI can automatically convert them into byte offsets as needed.
- // Maximum image dimensions are 65536 x 65536, because that will precisely fit the worst case start offset into uint32_t.
- // maxPixelCount = 65536² = 4294967296
- // maxStartOffset = maxPixelCount - 1 = 4294967295
- // largest uint32_t = 2³² - 1 = 4294967295
- // Because the computer will do bitwise operations to read and write small integers anyway,
- // there is usually no performance penalty for choosing an odd number of bits to pack more information.
- class ImageDimensions {
- private:
- // Bit masks and offsets for the properties that are packed into the same 64-bit integer.
- static const uint64_t readMask_width = 0b1111111111111111100000000000000000000000000000000000000000000000; // 0 zeroes, 17 ones, 47 zeroes
- static const uint32_t inputMask_width = 0b11111111111111111 ; // 17 ones
- static const int bitOffset_width = 47 ;
- static const uint64_t readMask_height = 0b0000000000000000011111111111111111000000000000000000000000000000; // 17 zeroes, 17 ones, 30 zeroes
- static const uint32_t inputMask_height = 0b11111111111111111 ; // 17 ones
- static const int bitOffset_height = 30 ;
- static const uint64_t readMask_stride = 0b0000000000000000000000000000000000111111111111111110000000000000; // 34 zeroes, 17 ones, 13 zeroes
- static const uint32_t inputMask_stride = 0b11111111111111111 ; // 17 ones
- static const int bitOffset_stride = 13 ;
- static const uint64_t readMask_packOrder = 0b0000000000000000000000000000000000000000000000000001100000000000; // 51 zeroes, 2 ones, 11 zeroes
- static const uint32_t inputMask_packOrder = 0b11 ; // 2 ones
- static const int bitOffset_packOrder = 11 ;
- static const uint64_t readMask_format = 0b0000000000000000000000000000000000000000000000000000011000000000; // 52 zeroes, 2 ones, 9 zeroes
- static const uint32_t inputMask_format = 0b11 ; // 2 ones
- static const int bitOffset_format = 9 ;
- static const uint64_t readMask_subImage = 0b0000000000000000000000000000000000000000000000000000000100000000; // 55 zeroes, 1 ones, 8 zeroes
- private:
- // Actual members.
- uint64_t data = 0;
- uint32_t pixelStartOffset = 0; // This one fits exactly into 32 bits, but we will have 32 more bits of padding.
- private:
- // Helper functions.
- static inline uint64_t readFrom64(const uint64_t &source, uint64_t readMask, uint32_t bitOffset) {
- return (source & readMask) >> bitOffset;
- }
- public:
- // Access to the data.
- inline uint32_t getWidth() const {
- return ImageDimensions::readFrom64(this->data, readMask_width, bitOffset_width);
- }
- inline uint32_t getHeight() const {
- return ImageDimensions::readFrom64(this->data, readMask_height, bitOffset_height);
- }
- inline uint32_t getPixelStride() const {
- return ImageDimensions::readFrom64(this->data, readMask_stride, bitOffset_stride);
- }
- inline PackOrderIndex getPackOrderIndex() const {
- return (PackOrderIndex)readFrom64(this->data, readMask_packOrder, bitOffset_packOrder);
- }
- inline PixelFormat getPixelFormat() const {
- return (PixelFormat)readFrom64(this->data, readMask_format, bitOffset_format);
- }
- inline bool isSubImage() const {
- // No need to shift the bit before normalizing, because anything else than zero becomes 1.
- return (this->data & readMask_subImage) != 0;
- }
- inline uint32_t getLog2PixelSize() const {
- // Shift the constants instead of the index to save a cycle.
- uint64_t shifterPixelFormatIndex = this->data & readMask_format;
- if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU8 << bitOffset_format)) {
- return 0;
- } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU16 << bitOffset_format)) {
- return 1;
- } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoF32 << bitOffset_format)) {
- return 2;
- } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::RgbaU8 << bitOffset_format)) {
- return 2;
- } else {
- return 0; // Unknown pixel format!
- }
- }
- inline uint32_t getPixelSize() const {
- // Shift the constants instead of the index to save a cycle.
- uint64_t shifterPixelFormatIndex = this->data & readMask_format;
- if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU8 << bitOffset_format)) {
- return 1;
- } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU16 << bitOffset_format)) {
- return 2;
- } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoF32 << bitOffset_format)) {
- return 4;
- } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::RgbaU8 << bitOffset_format)) {
- return 4;
- } else {
- return 0; // Unknown pixel format!
- }
- }
- inline uint32_t getPixelStartOffset() const {
- return this->pixelStartOffset;
- }
- inline uintptr_t getByteStartOffset() const {
- return uintptr_t(this->getPixelStartOffset()) << this->getLog2PixelSize();
- }
- inline uintptr_t getByteStride() const {
- return uintptr_t(this->getPixelStride()) << this->getLog2PixelSize();
- }
- // Constuction that truncates individual inputs in modulo, just to make sure that too large values do not affect other values and make debugging into a nightmare.
- ImageDimensions(uint32_t width, uint32_t height, uint32_t pixelStride, PackOrderIndex packOrderIndex, PixelFormat pixelFormat, uint32_t pixelStartOffset) noexcept
- : data(((uint64_t)( width & inputMask_width ) << bitOffset_width)
- | ((uint64_t)( height & inputMask_height ) << bitOffset_height)
- | ((uint64_t)( pixelStride & inputMask_stride ) << bitOffset_stride)
- | ((uint64_t)(((uint32_t)packOrderIndex) & inputMask_packOrder ) << bitOffset_packOrder)
- | ((uint64_t)(((uint32_t)pixelFormat) & inputMask_format ) << bitOffset_format)
- ), pixelStartOffset(pixelStartOffset) {}
- ImageDimensions() {}
- void setWidthHeightStartSubImage(uint32_t width, uint32_t height, uint32_t pixelStartOffset) {
- this->data = (this->data & ~(readMask_width | readMask_height))
- | ((uint64_t)(width & inputMask_width ) << bitOffset_width)
- | ((uint64_t)(height & inputMask_height ) << bitOffset_height)
- | readMask_subImage;
- this->pixelStartOffset = pixelStartOffset;
- }
- };
- #define IMPL_IMAGE_CONSTRUCTORS(NEW_TYPE, BASE_TYPE) \
- NEW_TYPE() {} \
- NEW_TYPE(const Buffer &buffer, ImageDimensions dimensions) : BASE_TYPE(buffer, dimensions) {} \
- NEW_TYPE(const NEW_TYPE &source) : BASE_TYPE(source.impl_buffer, source.impl_dimensions) {} \
- NEW_TYPE(const NEW_TYPE &source, const IRect ®ion) \
- : BASE_TYPE(source.impl_buffer, source.impl_dimensions) { \
- IRect cut = IRect::cut(IRect(0, 0, source.impl_dimensions.getWidth(), source.impl_dimensions.getHeight()), region); \
- if (cut.hasArea()) { \
- this->impl_dimensions.setWidthHeightStartSubImage(cut.width(), cut.height(), source.impl_dimensions.getPixelStartOffset() + cut.left() + cut.top() * source.impl_dimensions.getPixelStride()); \
- } else { \
- this->impl_buffer = Buffer(); \
- this->impl_dimensions = ImageDimensions(); \
- } \
- }
- #define IMPL_IMAGE_HIGHER_CONSTRUCTORS(NEW_TYPE, BASE_TYPE) \
- IMPL_IMAGE_CONSTRUCTORS(NEW_TYPE, BASE_TYPE) \
- NEW_TYPE(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex) \
- : BASE_TYPE(buffer, pixelStartOffset, width, height, pixelStride, packOrderIndex) {}
- // Use imageAPI.h to access the content of images!
- // The content may change between library versions but is public to simplify access for inlined getters.
- struct Image {
- // PRIVATE:
- // To maintain encapsulation from version specific details, do not touch things starting with IMPL_ or impl_.
- // Use the inlined getters in imageAPI.h instead.
- // Reference counted pointer to the pixel data.
- Buffer impl_buffer;
- // Dimensions and pack order of the image.
- ImageDimensions impl_dimensions;
- // New.
- Image(const Buffer &buffer, ImageDimensions dimensions)
- : impl_buffer(buffer), impl_dimensions(dimensions) {}
- // Generic cut.
- Image(const Image &source, const IRect ®ion)
- : impl_buffer(source.impl_buffer), impl_dimensions(source.impl_dimensions) {
- IRect cut = IRect::cut(IRect(0, 0, source.impl_dimensions.getWidth(), source.impl_dimensions.getHeight()), region);
- if (cut.hasArea()) {
- this->impl_dimensions.setWidthHeightStartSubImage(cut.width(), cut.height(), source.impl_dimensions.getPixelStartOffset() + cut.left() + cut.top() * source.impl_dimensions.getPixelStride());
- } else {
- this->impl_buffer = Buffer();
- this->impl_dimensions = ImageDimensions();
- }
- }
- // Empty.
- Image() {}
- };
- // Can be unaligned.
- // Is not allowed to overwrite padding bytes, because it does not know the difference between padding and pixels belonging to a larger image sharing the same pixel buffer.
- struct ImageU8
- : public Image {
- static const int impl_pixelSize = 1;
- ImageU8(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
- : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::MonoU8, pixelStartOffset)) {}
- IMPL_IMAGE_CONSTRUCTORS(ImageU8, Image)
- };
- // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
- // Owns the padding bytes and may overwrite them during SIMD vectorization.
- struct AlignedImageU8
- : public ImageU8 {
- IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageU8, ImageU8)
- };
- // Can be unaligned.
- // Is not allowed to overwrite padding bytes, because it does not know the difference between padding and pixels belonging to a larger image sharing the same pixel buffer.
- struct ImageU16
- : public Image {
- static const int impl_pixelSize = 2;
- ImageU16(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
- : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::MonoU16, pixelStartOffset)) {}
- IMPL_IMAGE_CONSTRUCTORS(ImageU16, Image)
- };
- // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
- // Owns the padding bytes and may overwrite them during SIMD vectorization.
- struct AlignedImageU16
- : public ImageU16 {
- IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageU16, ImageU16)
- };
- // Can be unaligned.
- // Is not allowed to overwrite padding bytes, because it does not know the difference between padding and pixels belonging to a larger image sharing the same pixel buffer.
- struct ImageF32
- : public Image {
- static const int impl_pixelSize = 4;
- ImageF32(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
- : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::MonoF32, pixelStartOffset)) {}
- IMPL_IMAGE_CONSTRUCTORS(ImageF32, Image)
- };
- // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
- // Owns the padding bytes and may overwrite them during SIMD vectorization.
- struct AlignedImageF32
- : public ImageF32 {
- IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageF32, ImageF32)
- };
- // Can be unaligned.
- // Is not allowed to overwrite padding bytes, because it does not know the difference between padding and pixels belonging to a larger image sharing the same pixel buffer.
- // Can have any pack order.
- struct ImageRgbaU8
- : public Image {
- static const int impl_pixelSize = 4;
- ImageRgbaU8(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
- : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::RgbaU8, pixelStartOffset)) {}
- IMPL_IMAGE_CONSTRUCTORS(ImageRgbaU8, Image)
- };
- // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
- // Owns the padding bytes and may overwrite them during SIMD vectorization.
- // Can have any pack order.
- struct AlignedImageRgbaU8
- : public ImageRgbaU8 {
- IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageRgbaU8, ImageRgbaU8)
- };
- // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
- // Owns the padding bytes and may overwrite them during SIMD vectorization.
- // Always in RGBA order.
- struct OrderedImageRgbaU8
- : public AlignedImageRgbaU8 {
- IMPL_IMAGE_HIGHER_CONSTRUCTORS(OrderedImageRgbaU8, AlignedImageRgbaU8)
- };
- #undef IMPL_IMAGE_CONSTRUCTORS
- #undef IMPL_IMAGE_HIGHER_CONSTRUCTORS
- }
- #endif
|