imageAPI.h 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. 
  2. // zlib open source license
  3. //
  4. // Copyright (c) 2017 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. // Everything stored directly in the image types is immutable to allow value types to behave like reference types using the data that they point to.
  25. // Image types can not be dynamically casted, because the inheritance is entirely static without any virtual functions.
  26. #ifndef DFPSR_API_IMAGE
  27. #define DFPSR_API_IMAGE
  28. #include "../implementation/image/Image.h"
  29. #include "../implementation/image/Color.h"
  30. #include "../implementation/math/scalar.h"
  31. #include "../base/heap.h"
  32. namespace dsr {
  33. // Constructors
  34. // Pre-conditions:
  35. // 1 <= width <= 65536
  36. // 1 <= height <= 65536
  37. // Post-condition:
  38. // Returns a new image of width x height pixels, or an empty image on failure.
  39. // Warning: Setting zeroed to false will initialize the image with non-deterministic pixel data.
  40. // This can make execution faster but also complicate tracking down errors.
  41. // Only recommended for temporary images when you know for sure that all content will be overwritten.
  42. AlignedImageU8 image_create_U8(int32_t width, int32_t height, bool zeroed = true);
  43. AlignedImageU16 image_create_U16(int32_t width, int32_t height, bool zeroed = true);
  44. AlignedImageF32 image_create_F32(int32_t width, int32_t height, bool zeroed = true);
  45. OrderedImageRgbaU8 image_create_RgbaU8(int32_t width, int32_t height, bool zeroed = true);
  46. AlignedImageRgbaU8 image_create_RgbaU8_native(int32_t width, int32_t height, PackOrderIndex packOrderIndex, bool zeroed = true);
  47. // Properties
  48. // Returns image's width in pixels, or 0 from an empty image
  49. inline int32_t image_getWidth(const Image& image) { return image.impl_dimensions.getWidth(); }
  50. // Returns image's height in pixels, or 0 from an empty image
  51. inline int32_t image_getHeight(const Image& image) { return image.impl_dimensions.getHeight(); }
  52. // Stride is the offset from the beginning of one row to another.
  53. // May be larger than the image's width to align with cache lines or share pixel data with a wider image.
  54. // When you add a variable offset to a pointer in C++, the added offset is multiplied by the element size because the address is always stored in bytes.
  55. // Because all pixels have a power of two size, the multiplication can be optimized into a bit shift.
  56. // On ARM, adding whole elements to a pointer is just as fast as adding bytes, by shifting and adding in the same instruction.
  57. // On Intel/AMD, shifting and adding needs two instructions, so then it makes sense to pre-calculate the stride as bytes and cast to uint8_t* when adding.
  58. // Returns image's stride in whole pixels, or 0 from an empty image
  59. // Used when incrementing indices instead of pointers.
  60. inline int32_t image_getPixelStride(const Image& image) { return (intptr_t)image.impl_dimensions.getPixelStride(); }
  61. // Returns image's stride in bytes, or 0 from an empty image.
  62. inline int32_t image_getStride(const Image& image) { return (image_getPixelStride(image) << (uintptr_t)image.impl_dimensions.getLog2PixelSize()); }
  63. inline int32_t image_getStride(const ImageU8& image) { return image_getPixelStride(image); } // pixelStride * sizeof(uint8_t )
  64. inline int32_t image_getStride(const ImageU16& image) { return image_getPixelStride(image) << 1; } // pixelStride * sizeof(uint16_t)
  65. inline int32_t image_getStride(const ImageF32& image) { return image_getPixelStride(image) << 2; } // pixelStride * sizeof(float )
  66. inline int32_t image_getStride(const ImageRgbaU8& image) { return image_getPixelStride(image) << 2; } // pixelStride * sizeof(uint32_t)
  67. // Returns image's offset from the allocation start in whole pixels, or 0 from an empty image
  68. inline int64_t image_getPixelStartOffset(const Image& image) { return (int64_t)image.impl_dimensions.getPixelStartOffset(); }
  69. // Returns image's offset from the allocation start in bytes, or 0 from an empty image
  70. inline int64_t image_getStartOffset(const ImageU8& image) { return image_getPixelStartOffset(image); } // pixelStartOffset * sizeof(uint8_t )
  71. inline int64_t image_getStartOffset(const ImageU16& image) { return image_getPixelStartOffset(image) << 1; } // pixelStartOffset * sizeof(uint16_t)
  72. inline int64_t image_getStartOffset(const ImageF32& image) { return image_getPixelStartOffset(image) << 2; } // pixelStartOffset * sizeof(float )
  73. inline int64_t image_getStartOffset(const ImageRgbaU8& image) { return image_getPixelStartOffset(image) << 2; } // pixelStartOffset * sizeof(uint32_t)
  74. // Get a rectangle from the image's dimensions with the top left corner set to (0, 0).
  75. // Useful for clipping to an image's bounds or subdividing space for a graphical user interface.
  76. // Returns IRect(0, 0, 0, 0) for empty images.
  77. inline IRect image_getBound(const Image& image) { return IRect(0, 0, image.impl_dimensions.getWidth(), image.impl_dimensions.getHeight()); }
  78. // Returns false on null, true otherwise.
  79. inline bool image_exists(const Image& image) { return image.impl_buffer.isNotNull(); }
  80. // TODO: Rename into image_getUseCount for easier use.
  81. // Returns the number of handles to the image.
  82. // References to a handle doesn't count, only when a handle is stored by value.
  83. inline uintptr_t image_useCount(const Image& image) { return image.impl_buffer.getUseCount(); }
  84. // Returns the image's pack order index.
  85. inline PackOrderIndex image_getPackOrderIndex(const ImageRgbaU8& image) { return image.impl_dimensions.getPackOrderIndex(); }
  86. // Returns the image's pack order, containing bit masks and offsets needed to pack and unpack colors.
  87. inline PackOrder image_getPackOrder(const ImageRgbaU8& image) { return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()); };
  88. // Returns true iff the pixel at (x, y) is inside of image.
  89. inline bool image_isPixelInside(const Image& image, int32_t x, int32_t y) {
  90. return x >= 0 && x < image_getWidth(image) && y >= 0 && y < image_getHeight(image);
  91. }
  92. // Returns the size of one pixel in bytes dynamically by looking it up.
  93. inline int32_t image_getPixelSize(const Image& image) { return image.impl_dimensions.getPixelSize(); }
  94. // Returns the size of one pixel in bytes statically from the type.
  95. template <typename T> int32_t image_getPixelSize() { return T::impl_pixelSize; }
  96. // Channel packing
  97. // Extract one channel
  98. AlignedImageU8 image_get_red (const ImageRgbaU8& image);
  99. AlignedImageU8 image_get_green(const ImageRgbaU8& image);
  100. AlignedImageU8 image_get_blue (const ImageRgbaU8& image);
  101. AlignedImageU8 image_get_alpha(const ImageRgbaU8& image);
  102. // Pack one channel
  103. OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, int32_t blue, int32_t alpha);
  104. OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, int32_t blue, int32_t alpha);
  105. OrderedImageRgbaU8 image_pack(int32_t red, int32_t green, const ImageU8& blue, int32_t alpha);
  106. OrderedImageRgbaU8 image_pack(int32_t red, int32_t green, int32_t blue, const ImageU8& alpha);
  107. // Pack two channels
  108. OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, int32_t blue, int32_t alpha);
  109. OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, const ImageU8& blue, int32_t alpha);
  110. OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, int32_t blue, const ImageU8& alpha);
  111. OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, const ImageU8& blue, int32_t alpha);
  112. OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, int32_t blue, const ImageU8& alpha);
  113. OrderedImageRgbaU8 image_pack(int32_t red, int32_t green, const ImageU8& blue, const ImageU8& alpha);
  114. // Pack three channels
  115. OrderedImageRgbaU8 image_pack(int32_t red, const ImageU8& green, const ImageU8& blue, const ImageU8& alpha);
  116. OrderedImageRgbaU8 image_pack(const ImageU8& red, int32_t green, const ImageU8& blue, const ImageU8& alpha);
  117. OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, int32_t blue, const ImageU8& alpha);
  118. OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, const ImageU8& blue, int32_t alpha);
  119. // Pack four channels
  120. OrderedImageRgbaU8 image_pack(const ImageU8& red, const ImageU8& green, const ImageU8& blue, const ImageU8& alpha);
  121. // Pack a color to draw with using the image's pack order, as it would be represented as a pixel in the buffer.
  122. inline uint32_t image_pack(const ImageRgbaU8& image, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
  123. return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).packRgba(red, green, blue, alpha);
  124. }
  125. // Saturate and pack a color for an image's pack order, as it would be represented as a pixel in the buffer.
  126. inline uint32_t image_saturateAndPack(const ImageRgbaU8& image, const ColorRgbaI32& color) {
  127. return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).saturateAndPackRgba(color);
  128. }
  129. // Truncate and pack a color for an image's pack order, as it would be represented as a pixel in the buffer.
  130. inline uint32_t image_truncateAndPack(const ImageRgbaU8& image, const ColorRgbaI32& color) {
  131. return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).truncateAndPackRgba(color);
  132. }
  133. // Unpack a color back into an expanded and ordered RGBA format.
  134. // packedColor is expressed in image's pack order.
  135. inline ColorRgbaI32 image_unpack(const ImageRgbaU8& image, uint32_t packedColor) {
  136. return PackOrder::getPackOrder(image.impl_dimensions.getPackOrderIndex()).unpackRgba(packedColor);
  137. }
  138. // Pixel access
  139. // Pre-condition:
  140. // The pixel at (x, y) must exist within the image, or else the program may crash.
  141. // image_isPixelInsize(image, x, y)
  142. // Post-condition:
  143. // Returns a reference to the pixel at (x, y) in image.
  144. inline uint8_t &image_accessPixel(const ImageU8& image, int32_t x, int32_t y) {
  145. uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
  146. return *(buffer_getSafeData<uint8_t>(image.impl_buffer, "ImageU8 pixel access buffer") + pixelOffset);
  147. }
  148. inline uint16_t &image_accessPixel(const ImageU16& image, int32_t x, int32_t y) {
  149. uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
  150. return *(buffer_getSafeData<uint16_t>(image.impl_buffer, "ImageU16 pixel access buffer") + pixelOffset);
  151. }
  152. inline float &image_accessPixel(const ImageF32& image, int32_t x, int32_t y) {
  153. uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
  154. return *(buffer_getSafeData<float>(image.impl_buffer, "ImageF32 pixel access buffer") + pixelOffset);
  155. }
  156. inline uint32_t &image_accessPixel(const ImageRgbaU8& image, int32_t x, int32_t y) {
  157. uintptr_t pixelOffset = image_getPixelStartOffset(image) + y * image_getPixelStride(image) + x;
  158. return *(buffer_getSafeData<uint32_t>(image.impl_buffer, "ImageRgbaU8 pixel access buffer") + pixelOffset);
  159. }
  160. // Write a pixel to an image.
  161. // Out of bound is ignored silently without writing.
  162. // Empty images will be ignored safely.
  163. // Packed is faster if the color can be packed in advance for multiple pixels or comes directly from an image of the same rgba order.
  164. // Saturated to 0..255
  165. inline void image_writePixel(const ImageU8& image, int32_t x, int32_t y, int32_t color) {
  166. if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = clamp(0, color, 255);
  167. }
  168. // Saturated to 0..65535
  169. inline void image_writePixel(const ImageU16& image, int32_t x, int32_t y, int32_t color) {
  170. if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = clamp(0, color, 65535);
  171. }
  172. // No saturation needed
  173. inline void image_writePixel(const ImageF32& image, int32_t x, int32_t y, float color) {
  174. if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = color;
  175. }
  176. // Saturated to 0..255 in all channels
  177. inline void image_writePixel(const ImageRgbaU8& image, int32_t x, int32_t y, const ColorRgbaI32& color) {
  178. if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = image_saturateAndPack(image, color);
  179. }
  180. // Pre-packed color using image_saturateAndPack to create the pixel in advance.
  181. inline void image_writePixel(const ImageRgbaU8& image, int32_t x, int32_t y, uint32_t packedColor) {
  182. if (image_isPixelInside(image, x, y)) image_accessPixel(image, x, y) = packedColor;
  183. }
  184. // Read a pixel from an image with a solid border outside.
  185. // Out of bound will return the border color.
  186. // The border color does not have to be constrained to the limits of pixel storage.
  187. // Empty images will return zero.
  188. inline int32_t image_readPixel_border(const ImageU8& image, int32_t x, int32_t y, int32_t border = 0) {
  189. if (!image_exists(image)) {
  190. return 0;
  191. } else if (image_isPixelInside(image, x, y)) {
  192. return image_accessPixel(image, x, y);
  193. } else {
  194. return border;
  195. }
  196. }
  197. // The packed version is identical to the unpacked version, so we make a wrapper for template functions to call.
  198. //inline int32_t image_readPixel_border_packed(const ImageU8& image, int32_t x, int32_t y, int32_t border = 0) { return image_readPixel_border(image, x, y, border); }
  199. inline int32_t image_readPixel_border(const ImageU16& image, int32_t x, int32_t y, int32_t border = 0) {
  200. if (!image_exists(image)) {
  201. return 0;
  202. } else if (image_isPixelInside(image, x, y)) {
  203. return image_accessPixel(image, x, y);
  204. } else {
  205. return border;
  206. }
  207. }
  208. //inline int32_t image_readPixel_border_packed(const ImageU16& image, int32_t x, int32_t y, int32_t border = 0) { return image_readPixel_border(image, x, y, border); }
  209. inline float image_readPixel_border(const ImageF32& image, int32_t x, int32_t y, float border = 0.0f) {
  210. if (!image_exists(image)) {
  211. return 0.0f;
  212. } else if (image_isPixelInside(image, x, y)) {
  213. return image_accessPixel(image, x, y);
  214. } else {
  215. return border;
  216. }
  217. }
  218. //inline float image_readPixel_border_packed(const ImageF32& image, int32_t x, int32_t y, int32_t border = 0) { return image_readPixel_border(image, x, y, border); }
  219. inline ColorRgbaI32 image_readPixel_border(const ImageRgbaU8& image, int32_t x, int32_t y, const ColorRgbaI32& border = ColorRgbaI32()) {
  220. if (!image_exists(image)) {
  221. return ColorRgbaI32(0, 0, 0, 0);
  222. } else if (image_isPixelInside(image, x, y)) {
  223. return image_unpack(image, image_accessPixel(image, x, y));
  224. } else {
  225. return border;
  226. }
  227. }
  228. // Read the color directly as it is packed in image's pack order.
  229. inline uint32_t image_readPixel_border_packed(const ImageRgbaU8& image, int32_t x, int32_t y, uint32_t border = 0) {
  230. if (!image_exists(image)) {
  231. return 0;
  232. } else if (image_isPixelInside(image, x, y)) {
  233. return image_accessPixel(image, x, y);
  234. } else {
  235. return border;
  236. }
  237. }
  238. // Read a pixel from an image stretched edges.
  239. // Out of bound will return the closest pixel.
  240. // Empty images will return zero.
  241. inline uint8_t image_readPixel_clamp(const ImageU8& image, int32_t x, int32_t y) {
  242. if (image_exists(image)) {
  243. return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
  244. } else {
  245. return 0;
  246. }
  247. }
  248. //inline uint8_t image_readPixel_clamp_packed(const ImageU8& image, int32_t x, int32_t y) { return image_readPixel_clamp(image, x, y); }
  249. inline uint16_t image_readPixel_clamp(const ImageU16& image, int32_t x, int32_t y) {
  250. if (image_exists(image)) {
  251. return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
  252. } else {
  253. return 0;
  254. }
  255. }
  256. //inline uint16_t image_readPixel_clamp_packed(const ImageU16& image, int32_t x, int32_t y) { return image_readPixel_clamp(image, x, y); }
  257. inline float image_readPixel_clamp(const ImageF32& image, int32_t x, int32_t y) {
  258. if (image_exists(image)) {
  259. return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
  260. } else {
  261. return 0.0f;
  262. }
  263. }
  264. //inline float image_readPixel_clamp_packed(const ImageF32& image, int32_t x, int32_t y) { return image_readPixel_clamp(image, x, y); }
  265. inline ColorRgbaI32 image_readPixel_clamp(const ImageRgbaU8& image, int32_t x, int32_t y) {
  266. if (image_exists(image)) {
  267. return image_unpack(image, image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1)));
  268. } else {
  269. return ColorRgbaI32();
  270. }
  271. }
  272. // Read the color directly as it is packed in image's pack order.
  273. inline uint32_t image_readPixel_clamp_packed(const ImageRgbaU8& image, int32_t x, int32_t y) {
  274. if (image_exists(image)) {
  275. return image_accessPixel(image, clamp(0, x, image_getWidth(image) - 1), clamp(0, y, image_getHeight(image) - 1));
  276. } else {
  277. return 0;
  278. }
  279. }
  280. // Read a pixel from an image with tiling.
  281. // Out of bound will take the coordinates in modulo of the size.
  282. // Empty images will return zero.
  283. inline uint8_t image_readPixel_tile(const ImageU8& image, int32_t x, int32_t y) {
  284. if (image_exists(image)) {
  285. return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
  286. } else {
  287. return 0;
  288. }
  289. }
  290. //inline uint8_t image_readPixel_tile_packed(const ImageU8& image, int32_t x, int32_t y) { return image_readPixel_tile(image, x, y); }
  291. inline uint16_t image_readPixel_tile(const ImageU16& image, int32_t x, int32_t y) {
  292. if (image_exists(image)) {
  293. return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
  294. } else {
  295. return 0;
  296. }
  297. }
  298. //inline uint16_t image_readPixel_tile_packed(const ImageU16& image, int32_t x, int32_t y) { return image_readPixel_tile(image, x, y); }
  299. inline float image_readPixel_tile(const ImageF32& image, int32_t x, int32_t y) {
  300. if (image_exists(image)) {
  301. return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
  302. } else {
  303. return 0.0f;
  304. }
  305. }
  306. //inline float image_readPixel_tile_packed(const ImageF32& image, int32_t x, int32_t y) { return image_readPixel_tile(image, x, y); }
  307. inline ColorRgbaI32 image_readPixel_tile(const ImageRgbaU8& image, int32_t x, int32_t y) {
  308. if (image_exists(image)) {
  309. return image_unpack(image, image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image))));
  310. } else {
  311. return ColorRgbaI32();
  312. }
  313. }
  314. // Read the color directly as it is packed in image's pack order.
  315. inline uint32_t image_readPixel_tile_packed(const ImageRgbaU8& image, int32_t x, int32_t y) {
  316. if (image_exists(image)) {
  317. return image_accessPixel(image, signedModulo(x, image_getWidth(image)), signedModulo(y, image_getHeight(image)));
  318. } else {
  319. return 0;
  320. }
  321. }
  322. // Loading
  323. // Load an image from a file by giving the filename including folder path and extension.
  324. // If mustExist is true, an exception will be raised on failure.
  325. // If mustExist is false, failure will return an empty handle.
  326. OrderedImageRgbaU8 image_load_RgbaU8(const ReadableString& filename, bool mustExist = true);
  327. // Load an image from a memory buffer, which can be loaded with file_loadBuffer to get the same result as loading directly from the file.
  328. // A convenient way of loading compressed images from larger files.
  329. // Failure will return an empty handle.
  330. OrderedImageRgbaU8 image_decode_RgbaU8(const Buffer& fileContent);
  331. // A faster and more flexible way to load compressed images from memory.
  332. // If you just want to point directly to a memory location to avoid allocating many small buffers, you can use a safe pointer and a size in bytes.
  333. // Failure will return an empty handle.
  334. OrderedImageRgbaU8 image_decode_RgbaU8(SafePointer<const uint8_t> data, int size);
  335. // Saving
  336. // Save the image to the path specified by filename and return true iff the operation was successful.
  337. // The file extension is case insensitive after the last dot in filename.
  338. // Accepted file extensions:
  339. // *.jpg or *.jpeg
  340. // *.png
  341. // *.tga or *.targa
  342. // *.bmp
  343. // If mustWork is true, an exception will be raised on failure.
  344. // If mustWork is false, failure will return false.
  345. // The optional quality setting goes from 1% to 100% and is at the maximum by default.
  346. bool image_save(const ImageRgbaU8 &image, const ReadableString& filename, bool mustWork = true, int quality = 100);
  347. // Save the image to a memory buffer.
  348. // Post-condition: Returns a buffer with the encoded image format as it would be saved to a file, or empty on failure.
  349. // No exceptions will be raised on failure, because an error message without a filename would not explain much.
  350. // The optional quality setting goes from 1% to 100% and is at the maximum by default.
  351. Buffer image_encode(const ImageRgbaU8 &image, ImageFileFormat format, int quality = 90);
  352. // Fill all pixels with a uniform color
  353. void image_fill(const ImageU8& image, int32_t color);
  354. void image_fill(const ImageU16& image, int32_t color);
  355. void image_fill(const ImageF32& image, float color);
  356. void image_fill(const ImageRgbaU8& image, const ColorRgbaI32& color);
  357. // Clone
  358. // Get a deep clone of an image's content while discarding any pack order, padding and texture pyramids.
  359. // If the input image had a different pack order, it will automatically be converted into RGBA to preserve the colors.
  360. AlignedImageU8 image_clone(const ImageU8& image);
  361. AlignedImageU16 image_clone(const ImageU16& image);
  362. AlignedImageF32 image_clone(const ImageF32& image);
  363. OrderedImageRgbaU8 image_clone(const ImageRgbaU8& image);
  364. // Returns a copy of the image without any padding, which means that alignment cannot be guaranteed.
  365. // The pack order is the same as the input, becuase it just copies the memory one row at a time to be fast.
  366. // Used when external image libraries don't allow giving stride as a separate argument.
  367. ImageRgbaU8 image_removePadding(const ImageRgbaU8& image);
  368. // Ascii images
  369. String image_toAscii(const ImageU8& image, const String &alphabet);
  370. String image_toAscii(const ImageU8& image);
  371. AlignedImageU8 image_fromAscii(const String &content);
  372. // Comparisons
  373. // Get the maximum pixelwise difference between two images of the same format, or the highest possible value on failure
  374. // Useful for regression tests
  375. uint8_t image_maxDifference(const ImageU8& imageA, const ImageU8& imageB);
  376. uint16_t image_maxDifference(const ImageU16& imageA, const ImageU16& imageB);
  377. float image_maxDifference(const ImageF32& imageA, const ImageF32& imageB);
  378. uint8_t image_maxDifference(const ImageRgbaU8& imageA, const ImageRgbaU8& imageB);
  379. // TODO: Create sub-image constructors in the image types.
  380. // Sub-images are read/write views to a smaller region of the same pixel data.
  381. // Get a sub-image sharing buffer and side-effects with the parent image
  382. // Returns the overlapping region if out of bound
  383. // Returns a null image if there are no overlapping pixels to return
  384. inline ImageU8 image_getSubImage(const ImageU8& image, const IRect& region) {
  385. static_assert(sizeof(ImageU8) == sizeof(Image), "ImageU8 must have the same size as Image, to prevent slicing in assignments!");
  386. return ImageU8(image, region);
  387. }
  388. inline ImageU16 image_getSubImage(const ImageU16& image, const IRect& region) {
  389. static_assert(sizeof(ImageU16) == sizeof(Image), "ImageU16 must have the same size as Image, to prevent slicing in assignments!");
  390. return ImageU16(image, region);
  391. }
  392. inline ImageF32 image_getSubImage(const ImageF32& image, const IRect& region) {
  393. static_assert(sizeof(ImageF32) == sizeof(Image), "ImageF32 must have the same size as Image, to prevent slicing in assignments!");
  394. return ImageF32(image, region);
  395. }
  396. inline ImageRgbaU8 image_getSubImage(const ImageRgbaU8& image, const IRect& region) {
  397. static_assert(sizeof(ImageRgbaU8) == sizeof(Image), "ImageRgbaU8 must have the same size as Image, to prevent slicing in assignments!");
  398. return ImageRgbaU8(image, region);
  399. }
  400. // Check dynamically if the image was created as a sub-image.
  401. // Returns true if the image is a sub-image, created using image_getSubImage.
  402. // Returns false if the image is not a sub-image, created using image_create or default constructed as an empty image.
  403. inline bool image_isSubImage(const Image& image) {
  404. return image.impl_dimensions.isSubImage();
  405. }
  406. // Bound-checked pointer access (relatively safe compared to a raw pointer)
  407. // Returns a bound-checked pointer to the first pixel.
  408. template <typename T = uint8_t>
  409. inline SafePointer<uint8_t> image_getSafePointer(const ImageU8& image) {
  410. return image.impl_buffer.getSafe<uint8_t>("Pointer to ImageU8 pixels").increaseBytes(image_getStartOffset(image));
  411. }
  412. // Returns a bound-checked pointer to the first pixel at rowIndex.
  413. template <typename T = uint8_t>
  414. inline SafePointer<uint8_t> image_getSafePointer(const ImageU8& image, int32_t rowIndex) {
  415. return image_getSafePointer(image).increaseBytes(image_getStride(image) * rowIndex);
  416. }
  417. // Returns a bound-checked pointer to the first pixel.
  418. template <typename T = uint16_t>
  419. inline SafePointer<T> image_getSafePointer(const ImageU16& image) {
  420. return image.impl_buffer.getSafe<T>("Pointer to ImageU16 pixels").increaseBytes(image_getStartOffset(image));
  421. }
  422. // Returns a bound-checked pointer to the first pixel at rowIndex.
  423. template <typename T = uint16_t>
  424. inline SafePointer<T> image_getSafePointer(const ImageU16& image, int32_t rowIndex) {
  425. return image_getSafePointer<T>(image).increaseBytes(image_getStride(image) * rowIndex);
  426. }
  427. // Returns a bound-checked pointer to the first pixel.
  428. template <typename T = float>
  429. inline SafePointer<T> image_getSafePointer(const ImageF32& image) {
  430. return image.impl_buffer.getSafe<T>("Pointer to ImageF32 pixels").increaseBytes(image_getStartOffset(image));
  431. }
  432. // Returns a bound-checked pointer to the first pixel at rowIndex.
  433. template <typename T = float>
  434. inline SafePointer<T> image_getSafePointer(const ImageF32& image, int32_t rowIndex) {
  435. return image_getSafePointer<T>(image).increaseBytes(image_getStride(image) * rowIndex);
  436. }
  437. // Returns a bound-checked pointer to the first pixel.
  438. template <typename T = uint32_t>
  439. inline SafePointer<T> image_getSafePointer(const ImageRgbaU8& image) {
  440. return image.impl_buffer.getSafe<T>("Pointer to ImageRgbaU8 pixels").increaseBytes(image_getStartOffset(image));
  441. }
  442. // Returns a bound-checked pointer to the first pixel at rowIndex.
  443. template <typename T = uint32_t>
  444. inline SafePointer<T> image_getSafePointer(const ImageRgbaU8& image, int32_t rowIndex) {
  445. return image_getSafePointer<T>(image).increaseBytes(image_getStride(image) * rowIndex);
  446. }
  447. // Returns a bound-checked pointer to the first channel in the first pixel.
  448. inline SafePointer<uint8_t> image_getSafePointer_channels(const ImageRgbaU8& image) {
  449. return image.impl_buffer.getSafe<uint8_t>("Pointer to ImageRgbaU8 channels").increaseBytes(image_getStartOffset(image));
  450. }
  451. // Returns a bound-checked pointer to the first channel in the first pixel at rowIndex.
  452. inline SafePointer<uint8_t> image_getSafePointer_channels(const ImageRgbaU8& image, int32_t rowIndex) {
  453. return image.impl_buffer.getSafe<uint8_t>("Pointer to ImageRgbaU8 channels").increaseBytes(image_getStartOffset(image)).increaseBytes(image_getStride(image) * rowIndex);
  454. }
  455. // The dangerous image API
  456. // Use of these methods can be spotted using a search for "_dangerous_" in your code
  457. // Replaces the destructor in image's buffer, which.
  458. // newDestructor should not free the given data, only invoke destruction of any external resources that may depend on it before the data is freed automatically.
  459. inline void image_dangerous_replaceDestructor(ImageU8& image, const HeapDestructor &newDestructor) {
  460. if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
  461. }
  462. inline void image_dangerous_replaceDestructor(ImageU16& image, const HeapDestructor &newDestructor) {
  463. if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
  464. }
  465. inline void image_dangerous_replaceDestructor(ImageF32& image, const HeapDestructor &newDestructor) {
  466. if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
  467. }
  468. inline void image_dangerous_replaceDestructor(ImageRgbaU8& image, const HeapDestructor &newDestructor) {
  469. if (image_exists(image)) { return buffer_replaceDestructor(image.impl_buffer, newDestructor); }
  470. }
  471. // Returns a pointer to the image's pixels
  472. // Warning! Reading elements larger than 8 bits will have lower and higher bytes stored based on local endianness
  473. // Warning! Using bytes outside of the [0 .. stride * height - 1] range may cause crashes and undefined behaviour
  474. // Warning! Using the pointer after the image's lifetime may cause crashes from trying to access freed memory
  475. inline uint8_t* image_dangerous_getData(const ImageU8& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
  476. inline uint8_t* image_dangerous_getData(const ImageU16& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
  477. inline uint8_t* image_dangerous_getData(const ImageF32& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
  478. inline uint8_t* image_dangerous_getData(const ImageRgbaU8& image) { return image.impl_buffer.getUnsafe() + image_getStartOffset(image); }
  479. }
  480. #endif