imageAPI.cpp 30 KB

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