Image.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. 
  2. // zlib open source license
  3. //
  4. // Copyright (c) 2019 to 2025 David Forsgren Piuva
  5. //
  6. // This software is provided 'as-is', without any express or implied
  7. // warranty. In no event will the authors be held liable for any damages
  8. // arising from the use of this software.
  9. //
  10. // Permission is granted to anyone to use this software for any purpose,
  11. // including commercial applications, and to alter it and redistribute it
  12. // freely, subject to the following restrictions:
  13. //
  14. // 1. The origin of this software must not be misrepresented; you must not
  15. // claim that you wrote the original software. If you use this software
  16. // in a product, an acknowledgment in the product documentation would be
  17. // appreciated but is not required.
  18. //
  19. // 2. Altered source versions must be plainly marked as such, and must not be
  20. // misrepresented as being the original software.
  21. //
  22. // 3. This notice may not be removed or altered from any source
  23. // distribution.
  24. #ifndef DFPSR_IMAGE_TYPES
  25. #define DFPSR_IMAGE_TYPES
  26. #include "PackOrder.h"
  27. #include "../../math/IRect.h"
  28. #include "../../api/bufferAPI.h"
  29. namespace dsr {
  30. enum class ImageFileFormat {
  31. Unknown, // Used as an error code for unidentified formats.
  32. JPG, // Lossy compressed image format storing brightness separated from red and blue offsets using the discrete cosine transform of each block.
  33. 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.
  34. 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.
  35. BMP // Uncompressed image format for storing data that does not really represent an image and you just want it to be exact.
  36. };
  37. // Packed into 2 bits in ImageDimensions.
  38. enum class PixelFormat : uint32_t {
  39. MonoU8, // Gray-scale image of 8 bits per pixel (0..255).
  40. MonoU16,
  41. MonoF32,
  42. RgbaU8 // RGBA colors in any order. 8 bits per channel (0..255). 32 bits per pixel.
  43. };
  44. // Start offset and stride is stored in pixels and the getters in imageAPI can automatically convert them into byte offsets as needed.
  45. // Maximum image dimensions are 65536 x 65536, because that will precisely fit the worst case start offset into uint32_t.
  46. // maxPixelCount = 65536² = 4294967296
  47. // maxStartOffset = maxPixelCount - 1 = 4294967295
  48. // largest uint32_t = 2³² - 1 = 4294967295
  49. // Because the computer will do bitwise operations to read and write small integers anyway,
  50. // there is usually no performance penalty for choosing an odd number of bits to pack more information.
  51. class ImageDimensions {
  52. private:
  53. // Bit masks and offsets for the properties that are packed into the same 64-bit integer.
  54. static const uint64_t readMask_width = 0b1111111111111111100000000000000000000000000000000000000000000000; // 0 zeroes, 17 ones, 47 zeroes
  55. static const uint32_t inputMask_width = 0b11111111111111111 ; // 17 ones
  56. static const int bitOffset_width = 47 ;
  57. static const uint64_t readMask_height = 0b0000000000000000011111111111111111000000000000000000000000000000; // 17 zeroes, 17 ones, 30 zeroes
  58. static const uint32_t inputMask_height = 0b11111111111111111 ; // 17 ones
  59. static const int bitOffset_height = 30 ;
  60. static const uint64_t readMask_stride = 0b0000000000000000000000000000000000111111111111111110000000000000; // 34 zeroes, 17 ones, 13 zeroes
  61. static const uint32_t inputMask_stride = 0b11111111111111111 ; // 17 ones
  62. static const int bitOffset_stride = 13 ;
  63. static const uint64_t readMask_packOrder = 0b0000000000000000000000000000000000000000000000000001100000000000; // 51 zeroes, 2 ones, 11 zeroes
  64. static const uint32_t inputMask_packOrder = 0b11 ; // 2 ones
  65. static const int bitOffset_packOrder = 11 ;
  66. static const uint64_t readMask_format = 0b0000000000000000000000000000000000000000000000000000011000000000; // 52 zeroes, 2 ones, 9 zeroes
  67. static const uint32_t inputMask_format = 0b11 ; // 2 ones
  68. static const int bitOffset_format = 9 ;
  69. static const uint64_t readMask_subImage = 0b0000000000000000000000000000000000000000000000000000000100000000; // 55 zeroes, 1 ones, 8 zeroes
  70. private:
  71. // Actual members.
  72. uint64_t data = 0;
  73. uint32_t pixelStartOffset = 0; // This one fits exactly into 32 bits, but we will have 32 more bits of padding.
  74. private:
  75. // Helper functions.
  76. static inline uint64_t readFrom64(const uint64_t &source, uint64_t readMask, uint32_t bitOffset) {
  77. return (source & readMask) >> bitOffset;
  78. }
  79. public:
  80. // Access to the data.
  81. inline uint32_t getWidth() const {
  82. return ImageDimensions::readFrom64(this->data, readMask_width, bitOffset_width);
  83. }
  84. inline uint32_t getHeight() const {
  85. return ImageDimensions::readFrom64(this->data, readMask_height, bitOffset_height);
  86. }
  87. inline uint32_t getPixelStride() const {
  88. return ImageDimensions::readFrom64(this->data, readMask_stride, bitOffset_stride);
  89. }
  90. inline PackOrderIndex getPackOrderIndex() const {
  91. return (PackOrderIndex)readFrom64(this->data, readMask_packOrder, bitOffset_packOrder);
  92. }
  93. inline PixelFormat getPixelFormat() const {
  94. return (PixelFormat)readFrom64(this->data, readMask_format, bitOffset_format);
  95. }
  96. inline bool isSubImage() const {
  97. // No need to shift the bit before normalizing, because anything else than zero becomes 1.
  98. return (this->data & readMask_subImage) != 0;
  99. }
  100. inline uint32_t getLog2PixelSize() const {
  101. // Shift the constants instead of the index to save a cycle.
  102. uint64_t shifterPixelFormatIndex = this->data & readMask_format;
  103. if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU8 << bitOffset_format)) {
  104. return 0;
  105. } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU16 << bitOffset_format)) {
  106. return 1;
  107. } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoF32 << bitOffset_format)) {
  108. return 2;
  109. } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::RgbaU8 << bitOffset_format)) {
  110. return 2;
  111. } else {
  112. return 0; // Unknown pixel format!
  113. }
  114. }
  115. inline uint32_t getPixelSize() const {
  116. // Shift the constants instead of the index to save a cycle.
  117. uint64_t shifterPixelFormatIndex = this->data & readMask_format;
  118. if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU8 << bitOffset_format)) {
  119. return 1;
  120. } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoU16 << bitOffset_format)) {
  121. return 2;
  122. } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::MonoF32 << bitOffset_format)) {
  123. return 4;
  124. } else if (shifterPixelFormatIndex == ((uint64_t)PixelFormat::RgbaU8 << bitOffset_format)) {
  125. return 4;
  126. } else {
  127. return 0; // Unknown pixel format!
  128. }
  129. }
  130. inline uint32_t getPixelStartOffset() const {
  131. return this->pixelStartOffset;
  132. }
  133. inline uintptr_t getByteStartOffset() const {
  134. return uintptr_t(this->getPixelStartOffset()) << this->getLog2PixelSize();
  135. }
  136. inline uintptr_t getByteStride() const {
  137. return uintptr_t(this->getPixelStride()) << this->getLog2PixelSize();
  138. }
  139. // 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.
  140. ImageDimensions(uint32_t width, uint32_t height, uint32_t pixelStride, PackOrderIndex packOrderIndex, PixelFormat pixelFormat, uint32_t pixelStartOffset) noexcept
  141. : data(((uint64_t)( width & inputMask_width ) << bitOffset_width)
  142. | ((uint64_t)( height & inputMask_height ) << bitOffset_height)
  143. | ((uint64_t)( pixelStride & inputMask_stride ) << bitOffset_stride)
  144. | ((uint64_t)(((uint32_t)packOrderIndex) & inputMask_packOrder ) << bitOffset_packOrder)
  145. | ((uint64_t)(((uint32_t)pixelFormat) & inputMask_format ) << bitOffset_format)
  146. ), pixelStartOffset(pixelStartOffset) {}
  147. ImageDimensions() {}
  148. void setWidthHeightStartSubImage(uint32_t width, uint32_t height, uint32_t pixelStartOffset) {
  149. this->data = (this->data & ~(readMask_width | readMask_height))
  150. | ((uint64_t)(width & inputMask_width ) << bitOffset_width)
  151. | ((uint64_t)(height & inputMask_height ) << bitOffset_height)
  152. | readMask_subImage;
  153. this->pixelStartOffset = pixelStartOffset;
  154. }
  155. };
  156. #define IMPL_IMAGE_CONSTRUCTORS(NEW_TYPE, BASE_TYPE) \
  157. NEW_TYPE() {} \
  158. NEW_TYPE(const Buffer &buffer, ImageDimensions dimensions) : BASE_TYPE(buffer, dimensions) {} \
  159. NEW_TYPE(const NEW_TYPE &source) : BASE_TYPE(source.impl_buffer, source.impl_dimensions) {} \
  160. NEW_TYPE(const NEW_TYPE &source, const IRect &region) \
  161. : BASE_TYPE(source.impl_buffer, source.impl_dimensions) { \
  162. IRect cut = IRect::cut(IRect(0, 0, source.impl_dimensions.getWidth(), source.impl_dimensions.getHeight()), region); \
  163. if (cut.hasArea()) { \
  164. this->impl_dimensions.setWidthHeightStartSubImage(cut.width(), cut.height(), source.impl_dimensions.getPixelStartOffset() + cut.left() + cut.top() * source.impl_dimensions.getPixelStride()); \
  165. } else { \
  166. this->impl_buffer = Buffer(); \
  167. this->impl_dimensions = ImageDimensions(); \
  168. } \
  169. }
  170. #define IMPL_IMAGE_HIGHER_CONSTRUCTORS(NEW_TYPE, BASE_TYPE) \
  171. IMPL_IMAGE_CONSTRUCTORS(NEW_TYPE, BASE_TYPE) \
  172. NEW_TYPE(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex) \
  173. : BASE_TYPE(buffer, pixelStartOffset, width, height, pixelStride, packOrderIndex) {}
  174. // Use imageAPI.h to access the content of images!
  175. // The content may change between library versions but is public to simplify access for inlined getters.
  176. struct Image {
  177. // PRIVATE:
  178. // To maintain encapsulation from version specific details, do not touch things starting with IMPL_ or impl_.
  179. // Use the inlined getters in imageAPI.h instead.
  180. // Reference counted pointer to the pixel data.
  181. Buffer impl_buffer;
  182. // Dimensions and pack order of the image.
  183. ImageDimensions impl_dimensions;
  184. // New.
  185. Image(const Buffer &buffer, ImageDimensions dimensions)
  186. : impl_buffer(buffer), impl_dimensions(dimensions) {}
  187. // Generic cut.
  188. Image(const Image &source, const IRect &region)
  189. : impl_buffer(source.impl_buffer), impl_dimensions(source.impl_dimensions) {
  190. IRect cut = IRect::cut(IRect(0, 0, source.impl_dimensions.getWidth(), source.impl_dimensions.getHeight()), region);
  191. if (cut.hasArea()) {
  192. this->impl_dimensions.setWidthHeightStartSubImage(cut.width(), cut.height(), source.impl_dimensions.getPixelStartOffset() + cut.left() + cut.top() * source.impl_dimensions.getPixelStride());
  193. } else {
  194. this->impl_buffer = Buffer();
  195. this->impl_dimensions = ImageDimensions();
  196. }
  197. }
  198. // Empty.
  199. Image() {}
  200. };
  201. // Can be unaligned.
  202. // 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.
  203. struct ImageU8
  204. : public Image {
  205. static const int impl_pixelSize = 1;
  206. ImageU8(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
  207. : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::MonoU8, pixelStartOffset)) {}
  208. IMPL_IMAGE_CONSTRUCTORS(ImageU8, Image)
  209. };
  210. // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
  211. // Owns the padding bytes and may overwrite them during SIMD vectorization.
  212. struct AlignedImageU8
  213. : public ImageU8 {
  214. IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageU8, ImageU8)
  215. };
  216. // Can be unaligned.
  217. // 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.
  218. struct ImageU16
  219. : public Image {
  220. static const int impl_pixelSize = 2;
  221. ImageU16(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
  222. : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::MonoU16, pixelStartOffset)) {}
  223. IMPL_IMAGE_CONSTRUCTORS(ImageU16, Image)
  224. };
  225. // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
  226. // Owns the padding bytes and may overwrite them during SIMD vectorization.
  227. struct AlignedImageU16
  228. : public ImageU16 {
  229. IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageU16, ImageU16)
  230. };
  231. // Can be unaligned.
  232. // 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.
  233. struct ImageF32
  234. : public Image {
  235. static const int impl_pixelSize = 4;
  236. ImageF32(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
  237. : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::MonoF32, pixelStartOffset)) {}
  238. IMPL_IMAGE_CONSTRUCTORS(ImageF32, Image)
  239. };
  240. // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
  241. // Owns the padding bytes and may overwrite them during SIMD vectorization.
  242. struct AlignedImageF32
  243. : public ImageF32 {
  244. IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageF32, ImageF32)
  245. };
  246. // Can be unaligned.
  247. // 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.
  248. // Can have any pack order.
  249. struct ImageRgbaU8
  250. : public Image {
  251. static const int impl_pixelSize = 4;
  252. ImageRgbaU8(const Buffer &buffer, uint32_t pixelStartOffset, uint32_t width, uint32_t height, uint32_t pixelStride, const PackOrderIndex &packOrderIndex)
  253. : Image(buffer, ImageDimensions(width, height, pixelStride, packOrderIndex, PixelFormat::RgbaU8, pixelStartOffset)) {}
  254. IMPL_IMAGE_CONSTRUCTORS(ImageRgbaU8, Image)
  255. };
  256. // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
  257. // Owns the padding bytes and may overwrite them during SIMD vectorization.
  258. // Can have any pack order.
  259. struct AlignedImageRgbaU8
  260. : public ImageRgbaU8 {
  261. IMPL_IMAGE_HIGHER_CONSTRUCTORS(AlignedImageRgbaU8, ImageRgbaU8)
  262. };
  263. // The start of each row is aligned to DSR_MAXIMUM_ALIGNMENT for SIMD vectorization and thread safety.
  264. // Owns the padding bytes and may overwrite them during SIMD vectorization.
  265. // Always in RGBA order.
  266. struct OrderedImageRgbaU8
  267. : public AlignedImageRgbaU8 {
  268. IMPL_IMAGE_HIGHER_CONSTRUCTORS(OrderedImageRgbaU8, AlignedImageRgbaU8)
  269. };
  270. #undef IMPL_IMAGE_CONSTRUCTORS
  271. #undef IMPL_IMAGE_HIGHER_CONSTRUCTORS
  272. }
  273. #endif