imageAPI.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. 
  2. // zlib open source license
  3. //
  4. // Copyright (c) 2017 to 2019 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. #define DFPSR_INTERNAL_ACCESS
  25. #include <limits>
  26. #include <cassert>
  27. #include "imageAPI.h"
  28. #include "drawAPI.h"
  29. #include "fileAPI.h"
  30. #include "../image/draw.h"
  31. #include "../image/internal/imageInternal.h"
  32. #include "../image/stbImage/stbImageWrapper.h"
  33. #include "../math/scalar.h"
  34. using namespace dsr;
  35. // Constructors
  36. AlignedImageU8 dsr::image_create_U8(int32_t width, int32_t height) {
  37. return AlignedImageU8(std::make_shared<ImageU8Impl>(width, height));
  38. }
  39. AlignedImageU16 dsr::image_create_U16(int32_t width, int32_t height) {
  40. return AlignedImageU16(std::make_shared<ImageU16Impl>(width, height));
  41. }
  42. AlignedImageF32 dsr::image_create_F32(int32_t width, int32_t height) {
  43. return AlignedImageF32(std::make_shared<ImageF32Impl>(width, height));
  44. }
  45. OrderedImageRgbaU8 dsr::image_create_RgbaU8(int32_t width, int32_t height) {
  46. return OrderedImageRgbaU8(std::make_shared<ImageRgbaU8Impl>(width, height));
  47. }
  48. AlignedImageRgbaU8 dsr::image_create_RgbaU8_native(int32_t width, int32_t height, PackOrderIndex packOrderIndex) {
  49. return AlignedImageRgbaU8(std::make_shared<ImageRgbaU8Impl>(width, height, packOrderIndex));
  50. }
  51. // Loading from data pointer
  52. OrderedImageRgbaU8 dsr::image_decode_RgbaU8(const SafePointer<uint8_t> data, int size, bool mustParse) {
  53. return image_stb_decode_RgbaU8(data, size, mustParse);
  54. }
  55. // Loading from buffer
  56. OrderedImageRgbaU8 dsr::image_decode_RgbaU8(const Buffer& fileContent, bool mustParse) {
  57. return image_decode_RgbaU8(buffer_getSafeData<uint8_t>(fileContent, "image file buffer"), buffer_getSize(fileContent), mustParse);
  58. }
  59. // Loading from file
  60. OrderedImageRgbaU8 dsr::image_load_RgbaU8(const String& filename, bool mustExist) {
  61. //return image_stb_load_RgbaU8(filename, mustExist);
  62. Buffer fileContent = file_loadBuffer(filename, mustExist);
  63. return image_decode_RgbaU8(fileContent, mustExist);
  64. }
  65. bool dsr::image_save(const ImageRgbaU8 &image, const String& filename) {
  66. return image_stb_save(image, filename);
  67. }
  68. #define GET_OPTIONAL(SOURCE,DEFAULT) \
  69. if (image) { \
  70. return SOURCE; \
  71. } else { \
  72. return DEFAULT; \
  73. }
  74. // Properties
  75. int32_t dsr::image_getWidth(const ImageU8& image) { GET_OPTIONAL(image->width, 0); }
  76. int32_t dsr::image_getWidth(const ImageU16& image) { GET_OPTIONAL(image->width, 0); }
  77. int32_t dsr::image_getWidth(const ImageF32& image) { GET_OPTIONAL(image->width, 0); }
  78. int32_t dsr::image_getWidth(const ImageRgbaU8& image) { GET_OPTIONAL(image->width, 0); }
  79. int32_t dsr::image_getHeight(const ImageU8& image) { GET_OPTIONAL(image->height, 0); }
  80. int32_t dsr::image_getHeight(const ImageU16& image) { GET_OPTIONAL(image->height, 0); }
  81. int32_t dsr::image_getHeight(const ImageF32& image) { GET_OPTIONAL(image->height, 0); }
  82. int32_t dsr::image_getHeight(const ImageRgbaU8& image) { GET_OPTIONAL(image->height, 0); }
  83. int32_t dsr::image_getStride(const ImageU8& image) { GET_OPTIONAL(image->stride, 0); }
  84. int32_t dsr::image_getStride(const ImageU16& image) { GET_OPTIONAL(image->stride, 0); }
  85. int32_t dsr::image_getStride(const ImageF32& image) { GET_OPTIONAL(image->stride, 0); }
  86. int32_t dsr::image_getStride(const ImageRgbaU8& image) { GET_OPTIONAL(image->stride, 0); }
  87. IRect dsr::image_getBound(const ImageU8& image) { GET_OPTIONAL(IRect(0, 0, image->width, image->height), IRect()); }
  88. IRect dsr::image_getBound(const ImageU16& image) { GET_OPTIONAL(IRect(0, 0, image->width, image->height), IRect()); }
  89. IRect dsr::image_getBound(const ImageF32& image) { GET_OPTIONAL(IRect(0, 0, image->width, image->height), IRect()); }
  90. IRect dsr::image_getBound(const ImageRgbaU8& image) { GET_OPTIONAL(IRect(0, 0, image->width, image->height), IRect()); }
  91. bool dsr::image_exists(const ImageU8& image) { GET_OPTIONAL(true, false); }
  92. bool dsr::image_exists(const ImageU16& image) { GET_OPTIONAL(true, false); }
  93. bool dsr::image_exists(const ImageF32& image) { GET_OPTIONAL(true, false); }
  94. bool dsr::image_exists(const ImageRgbaU8& image) { GET_OPTIONAL(true, false); }
  95. int dsr::image_useCount(const ImageU8& image) { return image.use_count(); }
  96. int dsr::image_useCount(const ImageU16& image) { return image.use_count(); }
  97. int dsr::image_useCount(const ImageF32& image) { return image.use_count(); }
  98. int dsr::image_useCount(const ImageRgbaU8& image) { return image.use_count(); }
  99. PackOrderIndex dsr::image_getPackOrderIndex(const ImageRgbaU8& image) {
  100. GET_OPTIONAL(image->packOrder.packOrderIndex, PackOrderIndex::RGBA);
  101. }
  102. // Texture
  103. void dsr::image_generatePyramid(ImageRgbaU8& image) {
  104. if (image) {
  105. image->generatePyramid();
  106. }
  107. }
  108. void dsr::image_removePyramid(ImageRgbaU8& image) {
  109. if (image) {
  110. image->removePyramid();
  111. }
  112. }
  113. bool dsr::image_hasPyramid(const ImageRgbaU8& image) {
  114. GET_OPTIONAL(image->texture.hasMipBuffer(), false);
  115. }
  116. bool dsr::image_isTexture(const ImageRgbaU8& image) {
  117. GET_OPTIONAL(image->isTexture(), false);
  118. }
  119. // Pixel access
  120. #define INSIDE_XY (x >= 0 && x < image->width && y >= 0 && y < image->height)
  121. #define CLAMP_XY \
  122. if (x < 0) { x = 0; } \
  123. if (y < 0) { y = 0; } \
  124. if (x >= image->width) { x = image->width - 1; } \
  125. if (y >= image->height) { y = image->height - 1; }
  126. #define TILE_XY \
  127. x = signedModulo(x, image->width); \
  128. y = signedModulo(y, image->height);
  129. void dsr::image_writePixel(ImageU8& image, int32_t x, int32_t y, int32_t color) {
  130. if (image) {
  131. if (INSIDE_XY) {
  132. if (color < 0) { color = 0; }
  133. if (color > 255) { color = 255; }
  134. ImageU8Impl::writePixel_unsafe(*image, x, y, color);
  135. }
  136. }
  137. }
  138. void dsr::image_writePixel(ImageU16& image, int32_t x, int32_t y, int32_t color) {
  139. if (image) {
  140. if (INSIDE_XY) {
  141. if (color < 0) { color = 0; }
  142. if (color > 65535) { color = 65535; }
  143. ImageU16Impl::writePixel_unsafe(*image, x, y, color);
  144. }
  145. }
  146. }
  147. void dsr::image_writePixel(ImageF32& image, int32_t x, int32_t y, float color) {
  148. if (image) {
  149. if (INSIDE_XY) {
  150. ImageF32Impl::writePixel_unsafe(*image, x, y, color);
  151. }
  152. }
  153. }
  154. void dsr::image_writePixel(ImageRgbaU8& image, int32_t x, int32_t y, const ColorRgbaI32& color) {
  155. if (image) {
  156. if (INSIDE_XY) {
  157. ImageRgbaU8Impl::writePixel_unsafe(*image, x, y, image->packRgba(color.saturate()));
  158. }
  159. }
  160. }
  161. int32_t dsr::image_readPixel_border(const ImageU8& image, int32_t x, int32_t y, int32_t border) {
  162. if (image) {
  163. if (INSIDE_XY) {
  164. return ImageU8Impl::readPixel_unsafe(*image, x, y);
  165. } else {
  166. return border;
  167. }
  168. } else {
  169. return 0;
  170. }
  171. }
  172. int32_t dsr::image_readPixel_border(const ImageU16& image, int32_t x, int32_t y, int32_t border) {
  173. if (image) {
  174. if (INSIDE_XY) {
  175. return ImageU16Impl::readPixel_unsafe(*image, x, y);
  176. } else {
  177. return border;
  178. }
  179. } else {
  180. return 0;
  181. }
  182. }
  183. float dsr::image_readPixel_border(const ImageF32& image, int32_t x, int32_t y, float border) {
  184. if (image) {
  185. if (INSIDE_XY) {
  186. return ImageF32Impl::readPixel_unsafe(*image, x, y);
  187. } else {
  188. return border;
  189. }
  190. } else {
  191. return 0.0f;
  192. }
  193. }
  194. ColorRgbaI32 dsr::image_readPixel_border(const ImageRgbaU8& image, int32_t x, int32_t y, const ColorRgbaI32& border) {
  195. if (image) {
  196. if (INSIDE_XY) {
  197. return image->unpackRgba(ImageRgbaU8Impl::readPixel_unsafe(*image, x, y));
  198. } else {
  199. return border; // Can return unsaturated colors as error codes
  200. }
  201. } else {
  202. return ColorRgbaI32();
  203. }
  204. }
  205. uint8_t dsr::image_readPixel_clamp(const ImageU8& image, int32_t x, int32_t y) {
  206. if (image) {
  207. CLAMP_XY;
  208. return ImageU8Impl::readPixel_unsafe(*image, x, y);
  209. } else {
  210. return 0;
  211. }
  212. }
  213. uint16_t dsr::image_readPixel_clamp(const ImageU16& image, int32_t x, int32_t y) {
  214. if (image) {
  215. CLAMP_XY;
  216. return ImageU16Impl::readPixel_unsafe(*image, x, y);
  217. } else {
  218. return 0;
  219. }
  220. }
  221. float dsr::image_readPixel_clamp(const ImageF32& image, int32_t x, int32_t y) {
  222. if (image) {
  223. CLAMP_XY;
  224. return ImageF32Impl::readPixel_unsafe(*image, x, y);
  225. } else {
  226. return 0.0f;
  227. }
  228. }
  229. ColorRgbaI32 dsr::image_readPixel_clamp(const ImageRgbaU8& image, int32_t x, int32_t y) {
  230. if (image) {
  231. CLAMP_XY;
  232. return image->unpackRgba(ImageRgbaU8Impl::readPixel_unsafe(*image, x, y));
  233. } else {
  234. return ColorRgbaI32();
  235. }
  236. }
  237. uint8_t dsr::image_readPixel_tile(const ImageU8& image, int32_t x, int32_t y) {
  238. if (image) {
  239. TILE_XY;
  240. return ImageU8Impl::readPixel_unsafe(*image, x, y);
  241. } else {
  242. return 0;
  243. }
  244. }
  245. uint16_t dsr::image_readPixel_tile(const ImageU16& image, int32_t x, int32_t y) {
  246. if (image) {
  247. TILE_XY;
  248. return ImageU16Impl::readPixel_unsafe(*image, x, y);
  249. } else {
  250. return 0;
  251. }
  252. }
  253. float dsr::image_readPixel_tile(const ImageF32& image, int32_t x, int32_t y) {
  254. if (image) {
  255. TILE_XY;
  256. return ImageF32Impl::readPixel_unsafe(*image, x, y);
  257. } else {
  258. return 0.0f;
  259. }
  260. }
  261. ColorRgbaI32 dsr::image_readPixel_tile(const ImageRgbaU8& image, int32_t x, int32_t y) {
  262. if (image) {
  263. TILE_XY;
  264. return image->unpackRgba(ImageRgbaU8Impl::readPixel_unsafe(*image, x, y));
  265. } else {
  266. return ColorRgbaI32();
  267. }
  268. }
  269. void dsr::image_fill(ImageU8& image, int32_t color) {
  270. if (image) {
  271. imageImpl_draw_solidRectangle(*image, imageInternal::getBound(*image), color);
  272. }
  273. }
  274. void dsr::image_fill(ImageU16& image, int32_t color) {
  275. if (image) {
  276. imageImpl_draw_solidRectangle(*image, imageInternal::getBound(*image), color);
  277. }
  278. }
  279. void dsr::image_fill(ImageF32& image, float color) {
  280. if (image) {
  281. imageImpl_draw_solidRectangle(*image, imageInternal::getBound(*image), color);
  282. }
  283. }
  284. void dsr::image_fill(ImageRgbaU8& image, const ColorRgbaI32& color) {
  285. if (image) {
  286. imageImpl_draw_solidRectangle(*image, imageInternal::getBound(*image), color);
  287. }
  288. }
  289. AlignedImageU8 dsr::image_clone(const ImageU8& image) {
  290. if (image) {
  291. AlignedImageU8 result = image_create_U8(image->width, image->height);
  292. draw_copy(result, image);
  293. return result;
  294. } else {
  295. return AlignedImageU8(); // Null gives null
  296. }
  297. }
  298. AlignedImageU16 dsr::image_clone(const ImageU16& image) {
  299. if (image) {
  300. AlignedImageU16 result = image_create_U16(image->width, image->height);
  301. draw_copy(result, image);
  302. return result;
  303. } else {
  304. return AlignedImageU16(); // Null gives null
  305. }
  306. }
  307. AlignedImageF32 dsr::image_clone(const ImageF32& image) {
  308. if (image) {
  309. AlignedImageF32 result = image_create_F32(image->width, image->height);
  310. draw_copy(result, image);
  311. return result;
  312. } else {
  313. return AlignedImageF32(); // Null gives null
  314. }
  315. }
  316. OrderedImageRgbaU8 dsr::image_clone(const ImageRgbaU8& image) {
  317. if (image) {
  318. OrderedImageRgbaU8 result = image_create_RgbaU8(image->width, image->height);
  319. draw_copy(result, image);
  320. return result;
  321. } else {
  322. return OrderedImageRgbaU8(); // Null gives null
  323. }
  324. }
  325. ImageRgbaU8 dsr::image_removePadding(const ImageRgbaU8& image) {
  326. if (image) {
  327. // TODO: Copy the implementation of getWithoutPadding, to create ImageRgbaU8 directly
  328. return ImageRgbaU8(image->getWithoutPadding());
  329. } else {
  330. return ImageRgbaU8(); // Null gives null
  331. }
  332. }
  333. AlignedImageU8 dsr::image_get_red(const ImageRgbaU8& image) {
  334. if (image) {
  335. // TODO: Copy the implementation of getChannel, to create ImageU8 directly
  336. return AlignedImageU8(image->getChannel(image->packOrder.redIndex));
  337. } else {
  338. return AlignedImageU8(); // Null gives null
  339. }
  340. }
  341. AlignedImageU8 dsr::image_get_green(const ImageRgbaU8& image) {
  342. if (image) {
  343. // TODO: Copy the implementation of getChannel, to create ImageU8 directly
  344. return AlignedImageU8(image->getChannel(image->packOrder.greenIndex));
  345. } else {
  346. return AlignedImageU8(); // Null gives null
  347. }
  348. }
  349. AlignedImageU8 dsr::image_get_blue(const ImageRgbaU8& image) {
  350. if (image) {
  351. // TODO: Copy the implementation of getChannel, to create ImageU8 directly
  352. return AlignedImageU8(image->getChannel(image->packOrder.blueIndex));
  353. } else {
  354. return AlignedImageU8(); // Null gives null
  355. }
  356. }
  357. AlignedImageU8 dsr::image_get_alpha(const ImageRgbaU8& image) {
  358. if (image) {
  359. // TODO: Copy the implementation of getChannel, to create ImageU8 directly
  360. return AlignedImageU8(image->getChannel(image->packOrder.alphaIndex));
  361. } else {
  362. return AlignedImageU8(); // Null gives null
  363. }
  364. }
  365. static inline int32_t readColor(const ImageU8& channel, int x, int y) {
  366. return ImageU8Impl::readPixel_unsafe(*channel, x, y);
  367. }
  368. static inline int32_t readColor(int32_t color, int x, int y) {
  369. return color;
  370. }
  371. template <typename R, typename G, typename B, typename A>
  372. static OrderedImageRgbaU8 pack_template(int32_t width, int32_t height, R red, G green, B blue, A alpha) {
  373. OrderedImageRgbaU8 result = image_create_RgbaU8(width, height);
  374. for (int y = 0; y < height; y++) {
  375. for (int x = 0; x < width; x++) {
  376. ColorRgbaI32 color = ColorRgbaI32(readColor(red, x, y), readColor(green, x, y), readColor(blue, x, y), readColor(alpha, x, y));
  377. image_writePixel(result, x, y, color);
  378. }
  379. }
  380. return result;
  381. }
  382. #define PACK1(FIRST) \
  383. if (FIRST) { \
  384. return pack_template(FIRST->width, FIRST->height, red, green, blue, alpha); \
  385. } else { \
  386. return OrderedImageRgbaU8(); \
  387. }
  388. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, int32_t green, int32_t blue, int32_t alpha) { PACK1(red); }
  389. OrderedImageRgbaU8 dsr::image_pack(int32_t red, const ImageU8& green, int32_t blue, int32_t alpha) { PACK1(green); }
  390. OrderedImageRgbaU8 dsr::image_pack(int32_t red, int32_t green, const ImageU8& blue, int32_t alpha) { PACK1(blue); }
  391. OrderedImageRgbaU8 dsr::image_pack(int32_t red, int32_t green, int32_t blue, const ImageU8& alpha) { PACK1(alpha); }
  392. #define PACK2(FIRST,SECOND) \
  393. if (FIRST && SECOND) { \
  394. if (FIRST->width != SECOND->width || FIRST->height != SECOND->height) { \
  395. throwError("Cannot pack two channels of different size!\n"); \
  396. } \
  397. return pack_template(FIRST->width, FIRST->height, red, green, blue, alpha); \
  398. } else { \
  399. return OrderedImageRgbaU8(); \
  400. }
  401. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, const ImageU8& green, int32_t blue, int32_t alpha) { PACK2(red,green) }
  402. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, int32_t green, const ImageU8& blue, int32_t alpha) { PACK2(red,blue) }
  403. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, int32_t green, int32_t blue, const ImageU8& alpha) { PACK2(red,alpha) }
  404. OrderedImageRgbaU8 dsr::image_pack(int32_t red, const ImageU8& green, const ImageU8& blue, int32_t alpha) { PACK2(green,blue) }
  405. OrderedImageRgbaU8 dsr::image_pack(int32_t red, const ImageU8& green, int32_t blue, const ImageU8& alpha) { PACK2(green,alpha) }
  406. OrderedImageRgbaU8 dsr::image_pack(int32_t red, int32_t green, const ImageU8& blue, const ImageU8& alpha) { PACK2(blue,alpha) }
  407. #define PACK3(FIRST,SECOND,THIRD) \
  408. if (FIRST && SECOND && THIRD) { \
  409. if (FIRST->width != SECOND->width || FIRST->height != SECOND->height \
  410. || FIRST->width != THIRD->width || FIRST->height != THIRD->height) { \
  411. throwError("Cannot pack three channels of different size!\n"); \
  412. } \
  413. return pack_template(FIRST->width, FIRST->height, red, green, blue, alpha); \
  414. } else { \
  415. return OrderedImageRgbaU8(); \
  416. }
  417. OrderedImageRgbaU8 dsr::image_pack(int32_t red, const ImageU8& green, const ImageU8& blue, const ImageU8& alpha) { PACK3(green, blue, alpha) }
  418. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, int32_t green, const ImageU8& blue, const ImageU8& alpha) { PACK3(red, blue, alpha) }
  419. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, const ImageU8& green, int32_t blue, const ImageU8& alpha) { PACK3(red, green, alpha) }
  420. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, const ImageU8& green, const ImageU8& blue, int32_t alpha) { PACK3(red, green, blue) }
  421. // TODO: Optimize using zip instructions
  422. #define PACK4(FIRST,SECOND,THIRD,FOURTH) \
  423. if (FIRST && SECOND && THIRD && FOURTH) { \
  424. if (FIRST->width != SECOND->width || FIRST->height != SECOND->height \
  425. || FIRST->width != THIRD->width || FIRST->height != THIRD->height \
  426. || FIRST->width != FOURTH->width || FIRST->height != FOURTH->height) { \
  427. throwError("Cannot pack four channels of different size!\n"); \
  428. } \
  429. return pack_template(FIRST->width, FIRST->height, red, green, blue, alpha); \
  430. } else { \
  431. return OrderedImageRgbaU8(); \
  432. }
  433. OrderedImageRgbaU8 dsr::image_pack(const ImageU8& red, const ImageU8& green, const ImageU8& blue, const ImageU8& alpha) { PACK4(red, green, blue, alpha) }
  434. // Convert a grayscale image into an ascii image using the given alphabet.
  435. // Since all 256 characters cannot be in the alphabet, the encoding is lossy.
  436. // Each line is stored within <> to prevent text editors from removing meaningful white space.
  437. // The first line contains the given alphabet as a gradient from black to white.
  438. // Preconditions:
  439. // alphabet may not have extended ascii, non printable, '\', '"', '>' or linebreak
  440. // width <= stride
  441. // size of monochromeImage = height * stride
  442. // Example alphabet: " .,-_':;!+~=^?*abcdefghijklmnopqrstuvwxyz()[]{}|&@#0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  443. String dsr::image_toAscii(const ImageU8& image, const String& alphabet) {
  444. if (!image_exists(image)) {
  445. return U"null";
  446. }
  447. String result;
  448. char alphabetMap[256];
  449. int alphabetSize = string_length(alphabet);
  450. int width = image_getWidth(image);
  451. int height = image_getHeight(image);
  452. string_reserve(result, ((width + 4) * height) + alphabetSize + 5);
  453. double scale = (double)(alphabetSize - 1) / 255.0;
  454. double output = 0.49;
  455. for (int rawValue = 0; rawValue < 256; rawValue++) {
  456. int charIndex = (int)output;
  457. if (charIndex < 0) charIndex = 0;
  458. if (charIndex > alphabetSize - 1) charIndex = alphabetSize - 1;
  459. alphabetMap[rawValue] = alphabet[charIndex];
  460. output += scale;
  461. }
  462. string_appendChar(result, U'<');
  463. for (int charIndex = 0; charIndex < alphabetSize; charIndex++) {
  464. string_appendChar(result, alphabet[charIndex]);
  465. }
  466. string_append(result, U">\n");
  467. for (int y = 0; y < height; y++) {
  468. string_appendChar(result, U'<');
  469. for (int x = 0; x < width; x++) {
  470. string_appendChar(result, alphabetMap[image_readPixel_clamp(image, x, y)]);
  471. }
  472. string_append(result, U">\n");
  473. }
  474. return result;
  475. }
  476. String dsr::image_toAscii(const ImageU8& image) {
  477. return image_toAscii(image, U" .,-_':;!+~=^?*abcdefghijklmnopqrstuvwxyz()[]{}|&@#0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  478. }
  479. // Create a monochrome image from the ascii image in content.
  480. // String is used instead of ReadableString, so that the content can be decompressed from 8-bit strings in the binary.
  481. AlignedImageU8 dsr::image_fromAscii(const String& content) {
  482. char alphabet[128];
  483. uint8_t alphabetMap[128];
  484. char current;
  485. int x = 0;
  486. int y = -1;
  487. int width = 0;
  488. int height = 0;
  489. int alphabetSize = 0;
  490. int contentSize = string_length(content);
  491. bool quoted = false;
  492. int i = 0;
  493. while (i < contentSize && ((current = content[i]) != '\0')) {
  494. if (quoted) {
  495. if (y < 0) {
  496. if (current == '>') {
  497. quoted = false;
  498. y = 0;
  499. } else if (alphabetSize < 128) {
  500. alphabet[alphabetSize] = current;
  501. alphabetSize++;
  502. }
  503. } else {
  504. if (current == '>') {
  505. quoted = false;
  506. if (width < x) width = x;
  507. y++;
  508. x = 0;
  509. } else {
  510. x++;
  511. }
  512. }
  513. } else if (current == '<') {
  514. quoted = true;
  515. }
  516. i++;
  517. }
  518. if (alphabetSize < 2) {
  519. throwError(U"The alphabet needs at least two characters!");
  520. }
  521. height = y;
  522. if (x > 0) {
  523. throwError(U"All ascii images must end with a linebreak!");
  524. }
  525. for (i = 0; i < 128; i++) {
  526. alphabetMap[i] = 0;
  527. }
  528. for (i = 0; i < alphabetSize; i++) {
  529. int code = (int)(alphabet[i]);
  530. if (code < 32 || code > 126) {
  531. throwError(U"Ascii image contained non-printable standard ascii! Use codes 32 to 126.");
  532. }
  533. if (alphabetMap[code] > 0) {
  534. throwError(U"A character in the alphabet was used more than once!");
  535. }
  536. int value = (int)(((double)i) * (255.0f / ((double)(alphabetSize - 1))));
  537. if (value < 0) value = 0;
  538. if (value > 255) value = 255;
  539. alphabetMap[code] = value;
  540. }
  541. if (width <= 0 || height <= 0) {
  542. throwError(U"An ascii image had zero dimensions!");
  543. }
  544. AlignedImageU8 result = image_create_U8(width, height);
  545. x = 0; y = -1;
  546. quoted = false;
  547. i = 0;
  548. while (i < contentSize && ((current = content[i]) != '\0')) {
  549. if (quoted) {
  550. if (current == '>') {
  551. quoted = false;
  552. if (y >= 0 && x != width) {
  553. throwError(U"Lines in the ascii image do not have the same lengths.");
  554. }
  555. y++;
  556. x = 0;
  557. } else if (y >= 0) {
  558. int code = (int)current;
  559. if (code < 0) code = 0;
  560. if (code > 127) code = 127;
  561. image_writePixel(result, x, y, alphabetMap[code]);
  562. x++;
  563. }
  564. } else if (current == '<') {
  565. quoted = true;
  566. }
  567. i++;
  568. }
  569. return result;
  570. }
  571. // TODO: Try to recycle the memory to reduce overhead from heap allocating heads pointing to existing buffers
  572. template <typename IMAGE_TYPE, typename VALUE_TYPE>
  573. static inline IMAGE_TYPE subImage_template(const IMAGE_TYPE& image, const IRect& region) {
  574. if (image) {
  575. IRect cut = IRect::cut(imageInternal::getBound(*image), region);
  576. if (cut.hasArea()) {
  577. intptr_t newOffset = image->startOffset + (cut.left() * image->pixelSize) + (cut.top() * image->stride);
  578. return IMAGE_TYPE(std::make_shared<VALUE_TYPE>(cut.width(), cut.height(), image->stride, image->buffer, newOffset));
  579. }
  580. }
  581. return IMAGE_TYPE(); // Null if where are no overlapping pixels
  582. }
  583. template <typename IMAGE_TYPE, typename VALUE_TYPE>
  584. static inline IMAGE_TYPE subImage_template_withPackOrder(const IMAGE_TYPE& image, const IRect& region) {
  585. if (image) {
  586. IRect cut = IRect::cut(imageInternal::getBound(*image), region);
  587. if (cut.hasArea()) {
  588. intptr_t newOffset = image->startOffset + (cut.left() * image->pixelSize) + (cut.top() * image->stride);
  589. return IMAGE_TYPE(std::make_shared<VALUE_TYPE>(cut.width(), cut.height(), image->stride, image->buffer, newOffset, image->packOrder));
  590. }
  591. }
  592. return IMAGE_TYPE(); // Null if where are no overlapping pixels
  593. }
  594. ImageU8 dsr::image_getSubImage(const ImageU8& image, const IRect& region) {
  595. return subImage_template<ImageU8, ImageU8Impl>(image, region);
  596. }
  597. ImageU16 dsr::image_getSubImage(const ImageU16& image, const IRect& region) {
  598. return subImage_template<ImageU16, ImageU16Impl>(image, region);
  599. }
  600. ImageF32 dsr::image_getSubImage(const ImageF32& image, const IRect& region) {
  601. return subImage_template<ImageF32, ImageF32Impl>(image, region);
  602. }
  603. ImageRgbaU8 dsr::image_getSubImage(const ImageRgbaU8& image, const IRect& region) {
  604. return subImage_template_withPackOrder<ImageRgbaU8, ImageRgbaU8Impl>(image, region);
  605. }
  606. template <typename IMAGE_TYPE, int CHANNELS, typename ELEMENT_TYPE>
  607. ELEMENT_TYPE maxDifference_template(const IMAGE_TYPE& imageA, const IMAGE_TYPE& imageB) {
  608. if (imageA.width != imageB.width || imageA.height != imageB.height) {
  609. return std::numeric_limits<ELEMENT_TYPE>::max();
  610. } else {
  611. ELEMENT_TYPE maxDifference = 0;
  612. const SafePointer<ELEMENT_TYPE> rowDataA = imageInternal::getSafeData<ELEMENT_TYPE>(imageA);
  613. const SafePointer<ELEMENT_TYPE> rowDataB = imageInternal::getSafeData<ELEMENT_TYPE>(imageB);
  614. for (int y = 0; y < imageA.height; y++) {
  615. const SafePointer<ELEMENT_TYPE> pixelDataA = rowDataA;
  616. const SafePointer<ELEMENT_TYPE> pixelDataB = rowDataB;
  617. for (int x = 0; x < imageA.width; x++) {
  618. for (int c = 0; c < CHANNELS; c++) {
  619. ELEMENT_TYPE difference = absDiff(*pixelDataA, *pixelDataB);
  620. if (difference > maxDifference) {
  621. maxDifference = difference;
  622. }
  623. pixelDataA += 1;
  624. pixelDataB += 1;
  625. }
  626. }
  627. rowDataA.increaseBytes(imageA.stride);
  628. rowDataB.increaseBytes(imageB.stride);
  629. }
  630. return maxDifference;
  631. }
  632. }
  633. uint8_t dsr::image_maxDifference(const ImageU8& imageA, const ImageU8& imageB) {
  634. if (imageA && imageB) {
  635. return maxDifference_template<ImageU8Impl, 1, uint8_t>(*imageA, *imageB);
  636. } else {
  637. return std::numeric_limits<uint8_t>::infinity();
  638. }
  639. }
  640. uint16_t dsr::image_maxDifference(const ImageU16& imageA, const ImageU16& imageB) {
  641. if (imageA && imageB) {
  642. return maxDifference_template<ImageU16Impl, 1, uint16_t>(*imageA, *imageB);
  643. } else {
  644. return std::numeric_limits<uint16_t>::infinity();
  645. }
  646. }
  647. float dsr::image_maxDifference(const ImageF32& imageA, const ImageF32& imageB) {
  648. if (imageA && imageB) {
  649. return maxDifference_template<ImageF32Impl, 1, float>(*imageA, *imageB);
  650. } else {
  651. return std::numeric_limits<float>::infinity();
  652. }
  653. }
  654. uint8_t dsr::image_maxDifference(const ImageRgbaU8& imageA, const ImageRgbaU8& imageB) {
  655. if (imageA && imageB) {
  656. return maxDifference_template<ImageRgbaU8Impl, 4, uint8_t>(*imageA, *imageB);
  657. } else {
  658. return std::numeric_limits<uint8_t>::infinity();
  659. }
  660. }
  661. SafePointer<uint8_t> dsr::image_getSafePointer(const ImageU8& image, int rowIndex) {
  662. if (image) {
  663. return imageInternal::getSafeData<uint8_t>(image.get(), rowIndex);
  664. } else {
  665. return SafePointer<uint8_t>();
  666. }
  667. }
  668. SafePointer<uint16_t> dsr::image_getSafePointer(const ImageU16& image, int rowIndex) {
  669. if (image) {
  670. return imageInternal::getSafeData<uint16_t>(image.get(), rowIndex);
  671. } else {
  672. return SafePointer<uint16_t>();
  673. }
  674. }
  675. SafePointer<float> dsr::image_getSafePointer(const ImageF32& image, int rowIndex) {
  676. if (image) {
  677. return imageInternal::getSafeData<float>(image.get(), rowIndex);
  678. } else {
  679. return SafePointer<float>();
  680. }
  681. }
  682. SafePointer<uint32_t> dsr::image_getSafePointer(const ImageRgbaU8& image, int rowIndex) {
  683. if (image) {
  684. return imageInternal::getSafeData<uint32_t>(image.get(), rowIndex);
  685. } else {
  686. return SafePointer<uint32_t>();
  687. }
  688. }
  689. SafePointer<uint8_t> dsr::image_getSafePointer_channels(const ImageRgbaU8& image, int rowIndex) {
  690. if (image) {
  691. return imageInternal::getSafeData<uint8_t>(image.get(), rowIndex);
  692. } else {
  693. return SafePointer<uint8_t>();
  694. }
  695. }
  696. void dsr::image_dangerous_replaceDestructor(ImageU8& image, const std::function<void(uint8_t *)>& newDestructor) {
  697. if (image) { return buffer_replaceDestructor(image->buffer, newDestructor); }
  698. }
  699. void dsr::image_dangerous_replaceDestructor(ImageU16& image, const std::function<void(uint8_t *)>& newDestructor) {
  700. if (image) { return buffer_replaceDestructor(image->buffer, newDestructor); }
  701. }
  702. void dsr::image_dangerous_replaceDestructor(ImageF32& image, const std::function<void(uint8_t *)>& newDestructor) {
  703. if (image) { return buffer_replaceDestructor(image->buffer, newDestructor); }
  704. }
  705. void dsr::image_dangerous_replaceDestructor(ImageRgbaU8& image, const std::function<void(uint8_t *)>& newDestructor) {
  706. if (image) { return buffer_replaceDestructor(image->buffer, newDestructor); }
  707. }
  708. uint8_t* dsr::image_dangerous_getData(const ImageU8& image) {
  709. if (image) {
  710. return imageInternal::getSafeData<uint8_t>(*image).getUnsafe();
  711. } else {
  712. return nullptr;
  713. }
  714. }
  715. uint8_t* dsr::image_dangerous_getData(const ImageU16& image) {
  716. if (image) {
  717. return imageInternal::getSafeData<uint8_t>(*image).getUnsafe();
  718. } else {
  719. return nullptr;
  720. }
  721. }
  722. uint8_t* dsr::image_dangerous_getData(const ImageF32& image) {
  723. if (image) {
  724. return imageInternal::getSafeData<uint8_t>(*image).getUnsafe();
  725. } else {
  726. return nullptr;
  727. }
  728. }
  729. uint8_t* dsr::image_dangerous_getData(const ImageRgbaU8& image) {
  730. if (image) {
  731. return imageInternal::getSafeData<uint8_t>(*image).getUnsafe();
  732. } else {
  733. return nullptr;
  734. }
  735. }