MandelVideoGenerator.cpp 6.7 KB

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