04_more_image_formats.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #include <cstdio>
  2. #include <cerrno>
  3. #include <random>
  4. #include <string>
  5. #include <optional>
  6. #include <functional>
  7. #include "simple/graphical/initializer.h"
  8. #include "simple/graphical/software_window.h"
  9. #include "simple/graphical/algorithm/blit.h"
  10. #include "external/file.hpp"
  11. #include "external/picopng.h"
  12. #define STB_IMAGE_IMPLEMENTATION
  13. #include "external/stb_image.h"
  14. using namespace simple::graphical;
  15. // stb adapter
  16. surface stb_load_surface(const char* filename);
  17. // picopng adapter
  18. auto picopng_load_image(const char* filename)
  19. -> std::optional<std::pair<std::vector<unsigned char>, int2>>;
  20. void show_image(const surface& image);
  21. auto random_byte = std::bind(
  22. std::uniform_int_distribution<surface::byte>(),
  23. std::default_random_engine(std::random_device{}()) );
  24. int main(int argc, char const* argv[]) try
  25. {
  26. if(argc < 2)
  27. {
  28. std::puts("Image not specified.");
  29. return -1;
  30. }
  31. initializer init;
  32. // Usage of a unique pointer based adapter is very simple.
  33. surface stb_image(stb_load_surface(argv[1]));
  34. show_image(stb_image);
  35. // However the situation is different with owning containers, such as std::vector in picopng.
  36. auto picopng_image = picopng_load_image(argv[1]);
  37. if(picopng_image)
  38. {
  39. // You can use the pixel data as is, making sure the actual owner will outlive any such usage,
  40. surface image(
  41. picopng_image->first.data(),
  42. picopng_image->second,
  43. pixel_format(pixel_format::type::rgba32));
  44. show_image(image);
  45. // or copy the data.
  46. auto image_data = picopng_image->first;
  47. auto image_data_copy = std::make_unique<surface::byte[]>(image_data.size());
  48. std::copy(image_data.begin(), image_data.end(), image_data_copy.get());
  49. surface picopng_image_copy (
  50. std::move(image_data_copy),
  51. picopng_image->second,
  52. pixel_format(pixel_format::type::rgba32));
  53. show_image(picopng_image_copy);
  54. }
  55. else
  56. std::puts("Picopng failed"); // Picopng is expected to fail for formats other than png, that stb can handle.
  57. // Another possibility is, if the library can extract image information(format, size)
  58. // without loading it, and allows loading to arbitrary preallocated storage.
  59. // Lodepng (full version of picopng) might be an example of that kind of API, but not sure.
  60. // Here I just generate some random pixels as a demo.
  61. int api_width = stb_image.size().x();
  62. int api_height = stb_image.size().y();
  63. pixel_format format(pixel_format::type::rgb24);
  64. // Create a unique pointer
  65. auto data_size = api_width * api_height * format.bytes();
  66. auto image_data = std::make_unique<surface::byte[]>(data_size);
  67. std::generate(image_data.get(), image_data.get() + data_size, random_byte);
  68. // and pass it on,
  69. surface random_image(std::move(image_data), int2(api_width, api_height), format);
  70. show_image(random_image);
  71. // or create a surface and populate it in place.
  72. surface random_image2(int2(api_width, api_height), format);
  73. auto pixels = std::get<pixel_writer<rgb_pixel, surface::byte>>(random_image2.pixels());
  74. // Note that we have to do this row by row, since SDL might pad the pixels, which might or (more likely) might not be something that an image loading library supports
  75. surface::byte* row = pixels.row();
  76. for(int rows = pixels.raw_size().y(); rows --> 0; )
  77. {
  78. std::generate(row, row + pixels.raw_size().x(), std::ref(random_byte));
  79. row = pixels.next_row(row);
  80. }
  81. show_image(random_image2);
  82. return 0;
  83. }
  84. catch(...)
  85. {
  86. if(errno)
  87. std::perror("ERROR");
  88. const char* sdl_error = SDL_GetError();
  89. if(*sdl_error)
  90. std::puts(sdl_error);
  91. throw;
  92. }
  93. surface stb_load_surface(const char* filename)
  94. {
  95. using namespace std::literals;
  96. int2 size;
  97. int orig_format;
  98. auto data = stbi_load(filename, &size.x(), &size.y(), &orig_format, STBI_rgb_alpha);
  99. if(data == NULL)
  100. throw std::runtime_error("Loading image failed: "s + stbi_failure_reason());
  101. return
  102. {
  103. { data, [](surface::byte* data) { stbi_image_free(data); } },
  104. size,
  105. pixel_format(pixel_format::type::rgba32)
  106. };
  107. }
  108. auto picopng_load_image(const char* filename)
  109. -> std::optional<std::pair<std::vector<unsigned char>, int2>>
  110. {
  111. auto png = simple::file::dump( simple::file::bropex(filename) );
  112. std::vector<surface::byte> image_data;
  113. simple::geom::vector<unsigned long,2> image_size;
  114. int picopng_result = decodePNG(
  115. image_data, image_size.x(), image_size.y(),
  116. reinterpret_cast<unsigned char*>(png.data()), png.size());
  117. return 0 == picopng_result
  118. ? std::make_optional(std::make_pair(std::move(image_data), int2(image_size)))
  119. : std::nullopt;
  120. }
  121. void show_image(const surface& image)
  122. {
  123. software_window win("Image view", image.size(), window::flags::borderless);
  124. blit(image, win.surface());
  125. win.update();
  126. SDL_Delay(1313);
  127. }