imageAPI.cpp 26 KB

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