ImageExport.cpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #include "ImageExport.h"
  2. #include "Bitmap.h"
  3. #include <png.h>
  4. #include <cstdio>
  5. namespace alm
  6. {
  7. ImageExportException::ImageExportException(const std::string& err) :
  8. std::runtime_error{ err }
  9. {
  10. }
  11. void exportPng(const ImageExportInfo& iei, std::function<void(float)> progressCallback)
  12. {
  13. if (iei.generator == nullptr) {
  14. throw "no generator";
  15. }
  16. progressCallback(0.0f);
  17. mnd::MandelGenerator& generator = *iei.generator;
  18. FILE* file = fopen(iei.path.c_str(), "wb");
  19. if(!file)
  20. throw ImageExportException{ std::string("could not open file '") + iei.path + "'" };
  21. png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  22. if (!png)
  23. throw ImageExportException{ "error creating png write struct" };
  24. png_infop info = png_create_info_struct(png);
  25. if (!info)
  26. throw ImageExportException{ "error creating png info struct" };
  27. if (setjmp(png_jmpbuf(png))) {
  28. png_destroy_write_struct(&png, &info);
  29. throw ImageExportException{ "error while creating png" };
  30. }
  31. png_init_io(png, file);
  32. const long width = iei.drawInfo.bWidth;
  33. const long height = iei.drawInfo.bHeight;
  34. png_set_IHDR(
  35. png,
  36. info,
  37. width, height,
  38. 8,
  39. PNG_COLOR_TYPE_RGB,
  40. PNG_INTERLACE_NONE,
  41. PNG_COMPRESSION_TYPE_DEFAULT,
  42. PNG_FILTER_TYPE_DEFAULT
  43. );
  44. png_write_info(png, info);
  45. long chunkHeight = height / 20;
  46. if (chunkHeight < 1)
  47. chunkHeight = 1;
  48. while (width * chunkHeight > 512 * 512)
  49. chunkHeight /= 2;
  50. auto rowPointers = std::make_unique<png_byte*[]>(chunkHeight);
  51. for (long chunkY = 0; chunkY < height; chunkY += chunkHeight) {
  52. auto minimum = [] (const auto& a, const auto& b) { return a < b ? a : b; };
  53. long tmpHeight = minimum(chunkHeight, height - chunkY);
  54. mnd::MandelInfo chunkInfo = iei.drawInfo;
  55. chunkInfo.bHeight = tmpHeight;
  56. chunkInfo.view.y += chunkInfo.view.height * chunkY / height;
  57. chunkInfo.view.height *= mnd::Real(tmpHeight) / height;
  58. Bitmap<float> chunk(width, tmpHeight);
  59. generator.generate(chunkInfo, chunk.pixels.get());
  60. Bitmap<RGBColor> coloredChunk = chunk.map<RGBColor>([&iei] (float i) {
  61. return i >= iei.drawInfo.maxIter ? RGBColor{ 0, 0, 0 } : iei.gradient.get(i);
  62. });
  63. for (long i = 0; i < tmpHeight; i++) {
  64. rowPointers[i] = reinterpret_cast<png_byte*>(&coloredChunk.get(0, i));
  65. }
  66. png_write_rows(png, &rowPointers[0], tmpHeight);
  67. if (chunkY < height)
  68. progressCallback(100.0f * chunkY / height);
  69. }
  70. png_write_end(png, NULL);
  71. fclose(file);
  72. png_destroy_write_struct(&png, &info);
  73. progressCallback(100.0f);
  74. }
  75. }