imageAPI.h 29 KB

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