imageAPI.cpp 31 KB

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