test_image.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*************************************************************************/
  2. /* test_image.h */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #ifndef TEST_IMAGE_H
  31. #define TEST_IMAGE_H
  32. #include "core/io/image.h"
  33. #include "core/os/os.h"
  34. #include "tests/test_utils.h"
  35. #include "thirdparty/doctest/doctest.h"
  36. namespace TestImage {
  37. TEST_CASE("[Image] Instantiation") {
  38. Ref<Image> image = memnew(Image(8, 4, false, Image::FORMAT_RGBA8));
  39. CHECK_MESSAGE(
  40. !image->is_empty(),
  41. "An image created with specified size and format should not be empty at first.");
  42. CHECK_MESSAGE(
  43. image->is_invisible(),
  44. "A newly created image should be invisible.");
  45. CHECK_MESSAGE(
  46. !image->is_compressed(),
  47. "A newly created image should not be compressed.");
  48. CHECK(!image->has_mipmaps());
  49. PackedByteArray image_data = image->get_data();
  50. for (int i = 0; i < image_data.size(); i++) {
  51. CHECK_MESSAGE(
  52. image_data[i] == 0,
  53. "An image created without data specified should have its data zeroed out.");
  54. }
  55. Ref<Image> image_copy = memnew(Image());
  56. CHECK_MESSAGE(
  57. image_copy->is_empty(),
  58. "An image created without any specified size and format be empty at first.");
  59. image_copy->copy_internals_from(image);
  60. CHECK_MESSAGE(
  61. image->get_data() == image_copy->get_data(),
  62. "Duplicated images should have the same data.");
  63. image_data = image->get_data();
  64. Ref<Image> image_from_data = memnew(Image(8, 4, false, Image::FORMAT_RGBA8, image_data));
  65. CHECK_MESSAGE(
  66. image->get_data() == image_from_data->get_data(),
  67. "An image created from data of another image should have the same data of the original image.");
  68. }
  69. TEST_CASE("[Image] Saving and loading") {
  70. Ref<Image> image = memnew(Image(4, 4, false, Image::FORMAT_RGBA8));
  71. const String save_path_png = OS::get_singleton()->get_cache_path().path_join("image.png");
  72. const String save_path_exr = OS::get_singleton()->get_cache_path().path_join("image.exr");
  73. // Save PNG
  74. Error err;
  75. err = image->save_png(save_path_png);
  76. CHECK_MESSAGE(
  77. err == OK,
  78. "The image should be saved successfully as a .png file.");
  79. // Save EXR
  80. err = image->save_exr(save_path_exr, false);
  81. CHECK_MESSAGE(
  82. err == OK,
  83. "The image should be saved successfully as an .exr file.");
  84. // Load using load()
  85. Ref<Image> image_load = memnew(Image());
  86. err = image_load->load(save_path_png);
  87. CHECK_MESSAGE(
  88. err == OK,
  89. "The image should load successfully using load().");
  90. CHECK_MESSAGE(
  91. image->get_data() == image_load->get_data(),
  92. "The loaded image should have the same data as the one that got saved.");
  93. // Load BMP
  94. Ref<Image> image_bmp = memnew(Image());
  95. Ref<FileAccess> f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err);
  96. PackedByteArray data_bmp;
  97. data_bmp.resize(f_bmp->get_length() + 1);
  98. f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length());
  99. CHECK_MESSAGE(
  100. image_bmp->load_bmp_from_buffer(data_bmp) == OK,
  101. "The BMP image should load successfully.");
  102. // Load JPG
  103. Ref<Image> image_jpg = memnew(Image());
  104. Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err);
  105. PackedByteArray data_jpg;
  106. data_jpg.resize(f_jpg->get_length() + 1);
  107. f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length());
  108. CHECK_MESSAGE(
  109. image_jpg->load_jpg_from_buffer(data_jpg) == OK,
  110. "The JPG image should load successfully.");
  111. // Load WEBP
  112. Ref<Image> image_webp = memnew(Image());
  113. Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err);
  114. PackedByteArray data_webp;
  115. data_webp.resize(f_webp->get_length() + 1);
  116. f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length());
  117. CHECK_MESSAGE(
  118. image_webp->load_webp_from_buffer(data_webp) == OK,
  119. "The WEBP image should load successfully.");
  120. // Load PNG
  121. Ref<Image> image_png = memnew(Image());
  122. Ref<FileAccess> f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err);
  123. PackedByteArray data_png;
  124. data_png.resize(f_png->get_length() + 1);
  125. f_png->get_buffer(data_png.ptrw(), f_png->get_length());
  126. CHECK_MESSAGE(
  127. image_png->load_png_from_buffer(data_png) == OK,
  128. "The PNG image should load successfully.");
  129. // Load TGA
  130. Ref<Image> image_tga = memnew(Image());
  131. Ref<FileAccess> f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err);
  132. PackedByteArray data_tga;
  133. data_tga.resize(f_tga->get_length() + 1);
  134. f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length());
  135. CHECK_MESSAGE(
  136. image_tga->load_tga_from_buffer(data_tga) == OK,
  137. "The TGA image should load successfully.");
  138. }
  139. TEST_CASE("[Image] Basic getters") {
  140. Ref<Image> image = memnew(Image(8, 4, false, Image::FORMAT_LA8));
  141. CHECK(image->get_width() == 8);
  142. CHECK(image->get_height() == 4);
  143. CHECK(image->get_size() == Vector2(8, 4));
  144. CHECK(image->get_format() == Image::FORMAT_LA8);
  145. CHECK(image->get_used_rect() == Rect2i(0, 0, 0, 0));
  146. Ref<Image> image_get_rect = image->get_rect(Rect2i(0, 0, 2, 1));
  147. CHECK(image_get_rect->get_size() == Vector2(2, 1));
  148. }
  149. TEST_CASE("[Image] Resizing") {
  150. Ref<Image> image = memnew(Image(8, 8, false, Image::FORMAT_RGBA8));
  151. // Crop
  152. image->crop(4, 4);
  153. CHECK_MESSAGE(
  154. image->get_size() == Vector2(4, 4),
  155. "get_size() should return the correct size after cropping.");
  156. image->set_pixel(0, 0, Color(1, 1, 1, 1));
  157. // Resize
  158. for (int i = 0; i < 5; i++) {
  159. Ref<Image> image_resized = memnew(Image());
  160. image_resized->copy_internals_from(image);
  161. Image::Interpolation interpolation = static_cast<Image::Interpolation>(i);
  162. image_resized->resize(8, 8, interpolation);
  163. CHECK_MESSAGE(
  164. image_resized->get_size() == Vector2(8, 8),
  165. "get_size() should return the correct size after resizing.");
  166. CHECK_MESSAGE(
  167. image_resized->get_pixel(1, 1).a > 0,
  168. "Resizing an image should also affect its content.");
  169. }
  170. // shrink_x2()
  171. image->shrink_x2();
  172. CHECK_MESSAGE(
  173. image->get_size() == Vector2(2, 2),
  174. "get_size() should return the correct size after shrink_x2().");
  175. // resize_to_po2()
  176. Ref<Image> image_po_2 = memnew(Image(14, 28, false, Image::FORMAT_RGBA8));
  177. image_po_2->resize_to_po2();
  178. CHECK_MESSAGE(
  179. image_po_2->get_size() == Vector2(16, 32),
  180. "get_size() should return the correct size after resize_to_po2().");
  181. }
  182. TEST_CASE("[Image] Modifying pixels of an image") {
  183. Ref<Image> image = memnew(Image(3, 3, false, Image::FORMAT_RGBA8));
  184. image->set_pixel(0, 0, Color(1, 1, 1, 1));
  185. CHECK_MESSAGE(
  186. !image->is_invisible(),
  187. "Image should not be invisible after drawing on it.");
  188. CHECK_MESSAGE(
  189. image->get_pixelv(Vector2(0, 0)).is_equal_approx(Color(1, 1, 1, 1)),
  190. "Image's get_pixel() should return the same color value as the one being set with set_pixel() in the same position.");
  191. CHECK_MESSAGE(
  192. image->get_used_rect() == Rect2i(0, 0, 1, 1),
  193. "Image's get_used_rect should return the expected value, larger than Rect2i(0, 0, 0, 0) if it's visible.");
  194. image->set_pixelv(Vector2(0, 0), Color(0.5, 0.5, 0.5, 0.5));
  195. Ref<Image> image2 = memnew(Image(3, 3, false, Image::FORMAT_RGBA8));
  196. // Fill image with color
  197. image2->fill(Color(0.5, 0.5, 0.5, 0.5));
  198. for (int y = 0; y < image2->get_height(); y++) {
  199. for (int x = 0; x < image2->get_width(); x++) {
  200. CHECK_MESSAGE(
  201. image2->get_pixel(x, y).r > 0.49,
  202. "fill() should colorize all pixels of the image.");
  203. }
  204. }
  205. // Fill rect with color
  206. {
  207. const int img_width = 3;
  208. const int img_height = 3;
  209. Vector<Rect2i> rects;
  210. rects.push_back(Rect2i());
  211. rects.push_back(Rect2i(-5, -5, 3, 3));
  212. rects.push_back(Rect2i(img_width, 0, 12, 12));
  213. rects.push_back(Rect2i(0, img_height, 12, 12));
  214. rects.push_back(Rect2i(img_width + 1, img_height + 2, 12, 12));
  215. rects.push_back(Rect2i(1, 1, 1, 1));
  216. rects.push_back(Rect2i(0, 1, 2, 3));
  217. rects.push_back(Rect2i(-5, 0, img_width + 10, 2));
  218. rects.push_back(Rect2i(0, -5, 2, img_height + 10));
  219. rects.push_back(Rect2i(-1, -1, img_width + 1, img_height + 1));
  220. for (const Rect2i &rect : rects) {
  221. Ref<Image> img = memnew(Image(img_width, img_height, false, Image::FORMAT_RGBA8));
  222. CHECK_NOTHROW_MESSAGE(
  223. img->fill_rect(rect, Color(1, 1, 1, 1)),
  224. "fill_rect() shouldn't throw for any rect.");
  225. for (int y = 0; y < img->get_height(); y++) {
  226. for (int x = 0; x < img->get_width(); x++) {
  227. if (rect.abs().has_point(Point2(x, y))) {
  228. CHECK_MESSAGE(
  229. img->get_pixel(x, y).is_equal_approx(Color(1, 1, 1, 1)),
  230. "fill_rect() should colorize all image pixels within rect bounds.");
  231. } else {
  232. CHECK_MESSAGE(
  233. !img->get_pixel(x, y).is_equal_approx(Color(1, 1, 1, 1)),
  234. "fill_rect() shouldn't colorize any image pixel out of rect bounds.");
  235. }
  236. }
  237. }
  238. }
  239. }
  240. // Blend two images together
  241. image->blend_rect(image2, Rect2i(Vector2i(0, 0), image2->get_size()), Vector2i(0, 0));
  242. CHECK_MESSAGE(
  243. image->get_pixel(0, 0).a > 0.7,
  244. "blend_rect() should blend the alpha values of the two images.");
  245. CHECK_MESSAGE(
  246. image->get_used_rect().size == image->get_size(),
  247. "get_used_rect() should return the expected value, its Rect size should be the same as get_size() if there are no transparent pixels.");
  248. Ref<Image> image3 = memnew(Image(2, 2, false, Image::FORMAT_RGBA8));
  249. image3->set_pixel(0, 0, Color(0, 1, 0, 1));
  250. //blit_rect() two images together
  251. image->blit_rect(image3, Rect2i(Vector2i(0, 0), image3->get_size()), Vector2i(0, 0));
  252. CHECK_MESSAGE(
  253. image->get_pixel(0, 0).is_equal_approx(Color(0, 1, 0, 1)),
  254. "blit_rect() should replace old colors and not blend them.");
  255. CHECK_MESSAGE(
  256. !image->get_pixel(2, 2).is_equal_approx(Color(0, 1, 0, 1)),
  257. "blit_rect() should not affect the area of the image that is outside src_rect.");
  258. // Flip image
  259. image3->flip_x();
  260. CHECK(image3->get_pixel(1, 0).is_equal_approx(Color(0, 1, 0, 1)));
  261. CHECK_MESSAGE(
  262. image3->get_pixel(0, 0).is_equal_approx(Color(0, 0, 0, 0)),
  263. "flip_x() should not leave old pixels behind.");
  264. image3->flip_y();
  265. CHECK(image3->get_pixel(1, 1).is_equal_approx(Color(0, 1, 0, 1)));
  266. CHECK_MESSAGE(
  267. image3->get_pixel(1, 0).is_equal_approx(Color(0, 0, 0, 0)),
  268. "flip_y() should not leave old pixels behind.");
  269. }
  270. } // namespace TestImage
  271. #endif // TEST_IMAGE_H