MandelVideoGenerator.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #include "MandelVideoGenerator.h"
  2. #include "VideoStream.h"
  3. #include "Mandel.h"
  4. #include <thread>
  5. #include <cmath>
  6. #include <omp.h>
  7. using alm::Gradient;
  8. MandelVideoGenerator::MandelVideoGenerator(const ExportVideoInfo& evi) :
  9. evi{ evi }
  10. {
  11. }
  12. void MandelVideoGenerator::addProgressCallback(ProgressCallback pc)
  13. {
  14. progressCallbacks.push_back(std::move(pc));
  15. }
  16. void MandelVideoGenerator::generate(mnd::MandelGenerator& gen)
  17. {
  18. const long width = evi.mi.bWidth;
  19. const long height = evi.mi.bHeight;
  20. VideoStream vs(width, height, evi.path, evi.bitrate, evi.fps, evi.preset.c_str());
  21. mnd::Real x = evi.end.x + evi.end.width / 2;
  22. mnd::Real y = evi.end.y + evi.end.height / 2;
  23. mnd::Real w = evi.start.width;
  24. mnd::Real h = evi.start.height;
  25. mnd::Real bigW = mnd::Real("1e+300");
  26. double bigFac = 1.0;
  27. Bitmap<RGBColor> big;
  28. Bitmap<RGBColor> small;
  29. int64_t frameCounter = 0;
  30. const float oversizeFactor = 2;
  31. const float sqrFactor = sqrt(oversizeFactor);
  32. mnd::MandelInfo mi = evi.mi;
  33. mi.bHeight *= oversizeFactor;
  34. mi.bWidth *= oversizeFactor;
  35. bool first = true;
  36. double zoomFactor = ::pow(0.99, evi.zoomSpeed);
  37. double approxFrames = double(mnd::log(evi.end.width / evi.start.width) / mnd::log(zoomFactor));
  38. while(w > evi.end.width || h > evi.end.height) {
  39. if (bigW > sqrt(oversizeFactor) * w) {
  40. mi.view = mnd::MandelViewport{ x - w/2, y - h/2, w, h };
  41. Bitmap<float> raw{ long(width * oversizeFactor), long(height * oversizeFactor) };
  42. Bitmap<float> rawSmall{ long(width * oversizeFactor), long(height * oversizeFactor) };
  43. mi.view.zoomCenter(oversizeFactor);
  44. gen.generate(mi, rawSmall.pixels.get());
  45. if (first) {
  46. mi.view.zoomCenter(sqrt(oversizeFactor));
  47. gen.generate(mi, raw.pixels.get());
  48. small = raw.map<RGBColor>([&mi, this] (float i) {
  49. return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : evi.gradient.get(i);
  50. });
  51. first = false;
  52. }
  53. big = std::move(small);
  54. small = rawSmall.map<RGBColor>([&mi, this] (float i) {
  55. return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : evi.gradient.get(i);
  56. });
  57. printf("recalced\n");
  58. bigW = w;
  59. bigFac = 1.0;
  60. }
  61. vs.addFrame(overlay(big, small, evi.mi.bWidth, evi.mi.bHeight, bigFac, sqrt(oversizeFactor)));
  62. frameCounter++;
  63. float progress = float(frameCounter / approxFrames);
  64. MandelVideoProgressInfo mvpi{ frameCounter, progress * 100 };
  65. callCallbacks(mvpi);
  66. w *= zoomFactor;
  67. h *= zoomFactor;
  68. bigFac *= zoomFactor;
  69. }
  70. }
  71. void MandelVideoGenerator::callCallbacks(const MandelVideoProgressInfo& evi)
  72. {
  73. for (auto& pc : progressCallbacks) {
  74. pc(evi);
  75. }
  76. }
  77. inline RGBColor lerpColors(const RGBColor& a, const RGBColor& b, double lerp)
  78. {
  79. auto mklin = [] (double x) {
  80. return x;
  81. };
  82. auto unlin = [] (double x) {
  83. return x;
  84. };
  85. return RGBColor{
  86. uint8_t(a.r * lerp + b.r * (1 - lerp)),
  87. uint8_t(a.g * lerp + b.g * (1 - lerp)),
  88. uint8_t(a.b * lerp + b.b * (1 - lerp))
  89. };
  90. }
  91. inline RGBColor biliniear(const Bitmap<RGBColor>& img, double x, double y)
  92. {
  93. int xfloor = int(::floor(x));
  94. int yfloor = int(::floor(y));
  95. int xceil = int(::ceil(x));
  96. int yceil = int(::ceil(y));
  97. double xLerp = x - xfloor;
  98. double yLerp = y - yfloor;
  99. RGBColor samples[2][2] = {
  100. {
  101. img.get(xfloor, yfloor),
  102. img.get(xfloor, yceil),
  103. },
  104. {
  105. img.get(xceil, yfloor),
  106. img.get(xceil, yceil),
  107. }
  108. };
  109. double r = 0, g = 0, b = 0;
  110. auto mklin = [] (double x) {
  111. return x;
  112. };
  113. auto unlin = [] (double x) {
  114. return x;
  115. };
  116. r += (1 - xLerp) * (1 - yLerp) * mklin(samples[0][0].r);
  117. r += (1 - xLerp) * yLerp * mklin(samples[0][1].r);
  118. r += xLerp * (1 - yLerp) * mklin(samples[1][0].r);
  119. r += xLerp * yLerp * mklin(samples[1][1].r);
  120. g += (1 - xLerp) * (1 - yLerp) * mklin(samples[0][0].g);
  121. g += (1 - xLerp) * yLerp * mklin(samples[0][1].g);
  122. g += xLerp * (1 - yLerp) * mklin(samples[1][0].g);
  123. g += xLerp * yLerp * mklin(samples[1][1].g);
  124. b += (1 - xLerp) * (1 - yLerp) * mklin(samples[0][0].b);
  125. b += (1 - xLerp) * yLerp * mklin(samples[0][1].b);
  126. b += xLerp * (1 - yLerp) * mklin(samples[1][0].b);
  127. b += xLerp * yLerp * mklin(samples[1][1].b);
  128. return RGBColor{ uint8_t(unlin(r)), uint8_t(unlin(g)), uint8_t(unlin(b)) };
  129. }
  130. inline RGBColor nearest(const Bitmap<RGBColor>& img, double x, double y)
  131. {
  132. int xfloor = int(::floor(x));
  133. int yfloor = int(::floor(y));
  134. return img.get(xfloor, yfloor);
  135. }
  136. Bitmap<RGBColor> MandelVideoGenerator::overlay(const Bitmap<RGBColor>& outer,
  137. const Bitmap<RGBColor>& inner, long bw, long bh,
  138. double scale, double oversizeFactor)
  139. {
  140. printf("%lf\n", scale);
  141. Bitmap<RGBColor> ret{ bw, bh };
  142. double outerLeft = outer.width * (1 - scale / oversizeFactor / oversizeFactor) / 2;
  143. double outerTop = outer.height * (1 - scale / oversizeFactor / oversizeFactor) / 2;
  144. double outerWidth = outer.width * scale / oversizeFactor / oversizeFactor;
  145. double outerHeight = outer.height * scale / oversizeFactor / oversizeFactor;
  146. double innerLeft = outer.width * (1 - scale / oversizeFactor) / 2;
  147. double innerTop = outer.height * (1 - scale / oversizeFactor) / 2;
  148. double innerWidth = outer.width * scale / oversizeFactor;
  149. double innerHeight = outer.height * scale / oversizeFactor;
  150. auto before = std::chrono::high_resolution_clock::now();
  151. #if defined(_OPENMP)
  152. omp_set_num_threads(omp_get_num_procs());
  153. # pragma omp parallel for
  154. #endif
  155. for (int i = 0; i < ret.height; i++) {
  156. for (int j = 0; j < ret.width; j++) {
  157. double newJ = outerLeft + outerWidth * j / ret.width;
  158. double newI = outerTop + outerHeight * i / ret.height;
  159. RGBColor a = biliniear(outer, newJ, newI);
  160. double innJ = innerLeft + innerWidth * j / ret.width;
  161. double innI = innerTop + innerHeight * i / ret.height;
  162. RGBColor b = biliniear(inner, innJ, innI);
  163. double lerpVal = -::log(scale) / ::log(oversizeFactor);
  164. RGBColor lerped = lerpColors(b, a, lerpVal);
  165. ret.get(j, i) = lerped;
  166. }
  167. }
  168. auto after = std::chrono::high_resolution_clock::now();
  169. //printf("gradient applied in: %lld microseconds\n", std::chrono::duration_cast<std::chrono::microseconds>(after - before).count());
  170. fflush(stdout);
  171. /*for (int i = 0; i < ret.height * ret.width; i++) {
  172. ret.pixels[i] = outer.pixels[i];
  173. }*/
  174. return ret;
  175. }
  176. namespace alm
  177. {
  178. void exportVideo(const alm::VideoRecipe& vr, const std::string& path)
  179. {
  180. }
  181. }