MandelWidget.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. #include "MandelWidget.h"
  2. #include <cmath>
  3. using namespace mnd;
  4. #include <QOpenGLVertexArrayObject>
  5. Texture::Texture(const Bitmap<RGBColor>& bitmap) :
  6. context{ nullptr }
  7. {
  8. glGenTextures(1, &id);
  9. glBindTexture(GL_TEXTURE_2D, id);
  10. long lineLength = (bitmap.width * 3 + 3) & ~3;
  11. unsigned char* pixels = new unsigned char[lineLength * bitmap.height];
  12. for (int i = 0; i < bitmap.width; i++) {
  13. for (int j = 0; j < bitmap.height; j++) {
  14. int index = i * 3 + j * lineLength;
  15. RGBColor c = bitmap.get(i, j);
  16. pixels[index] = c.r;
  17. pixels[index + 1] = c.g;
  18. pixels[index + 2] = c.b;
  19. }
  20. }
  21. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(bitmap.width), int(bitmap.height), 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
  22. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  23. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  24. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  25. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  26. }
  27. Texture::Texture(const Bitmap<RGBColor>& bitmap, QOpenGLContext* context) :
  28. context{ context }
  29. {
  30. context->functions()->glGenTextures(1, &id);
  31. context->functions()->glBindTexture(GL_TEXTURE_2D, id);
  32. long lineLength = (bitmap.width * 3 + 3) & ~3;
  33. unsigned char* pixels = new unsigned char[lineLength * bitmap.height];
  34. for (int i = 0; i < bitmap.width; i++) {
  35. for (int j = 0; j < bitmap.height; j++) {
  36. int index = i * 3 + j * lineLength;
  37. RGBColor c = bitmap.get(i, j);
  38. pixels[index] = c.r;
  39. pixels[index + 1] = c.g;
  40. pixels[index + 2] = c.b;
  41. }
  42. }
  43. context->functions()->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, int(bitmap.width), int(bitmap.height), 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
  44. context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  45. context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  46. context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  47. context->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  48. }
  49. Texture::~Texture(void)
  50. {
  51. glDeleteTextures(1, &id);
  52. }
  53. void Texture::bind(void) const
  54. {
  55. glBindTexture(GL_TEXTURE_2D, id);
  56. }
  57. void Texture::drawRect(float x, float y, float width, float height)
  58. {
  59. glColor3ub(255, 255, 255);
  60. glEnable(GL_TEXTURE_2D);
  61. bind();
  62. glBegin(GL_TRIANGLE_STRIP);
  63. glTexCoord2f(0, 0);
  64. glVertex2f(x, y);
  65. glTexCoord2f(1, 0);
  66. glVertex2f(x + width, y);
  67. glTexCoord2f(0, 1);
  68. glVertex2f(x, y + height);
  69. glTexCoord2f(1, 1);
  70. glVertex2f(x + width, y + height);
  71. glEnd();
  72. glDisable(GL_TEXTURE_2D);
  73. }
  74. std::pair<int, int> TexGrid::getCellIndices(double x, double y)
  75. {
  76. return { int(x / dpp / 64), int(y / dpp / 64) };
  77. }
  78. std::pair<double, double> TexGrid::getPositions(int x, int y)
  79. {
  80. return { x * dpp * 64, y * dpp * 64 };
  81. }
  82. Texture* TexGrid::getCell(int i, int j)
  83. {
  84. auto cIt = cells.find({i, j});
  85. if (cIt != cells.end()) {
  86. return &cIt->second;
  87. }
  88. else {
  89. return nullptr;
  90. }
  91. }
  92. MandelV::MandelV(QOpenGLContext* context) :
  93. empty{ Bitmap<RGBColor>(1, 1) }
  94. {
  95. Bitmap<RGBColor> emp(8, 8);
  96. for(auto i = 0; i < emp.width; i++) {
  97. for(auto j = 0; j < emp.height; j++) {
  98. if((i + j) & 0x1) { // if i+j is odd
  99. emp.get(i, j) = RGBColor{ 255, 255, 255 };
  100. }
  101. else {
  102. emp.get(i, j) = RGBColor{ 120, 120, 120 };
  103. }
  104. }
  105. }
  106. context->makeCurrent(nullptr);
  107. empty = Texture(emp, context);
  108. }
  109. int MandelV::getLevel(double dpp) {
  110. return -int(::log2(dpp / 64));
  111. }
  112. double MandelV::getDpp(int level)
  113. {
  114. return ::pow(2, -level) * 64;
  115. }
  116. TexGrid& MandelV::getGrid(int level)
  117. {
  118. auto it = levels.find(level);
  119. if (it != levels.end())
  120. return it->second;
  121. else {
  122. levels.insert({ level, TexGrid(getDpp(level)) });
  123. return levels[level];
  124. }
  125. }
  126. void MandelV::paint(const mnd::MandelViewport& mvp)
  127. {
  128. this->empty.drawRect(0, 0, 100, 100);
  129. return;
  130. int width = 1024;
  131. double dpp = mvp.width / width;
  132. int level = getLevel(dpp);
  133. auto& grid = getGrid(level);
  134. double gw = getDpp(level) * 64;
  135. auto [left, top] = grid.getCellIndices(mvp.x, mvp.y);
  136. auto [right, bottom] = grid.getCellIndices(mvp.right(), mvp.bottom());
  137. for(int i = left; i <= right; i++) {
  138. for(int j = top; j <= bottom; j++) {
  139. auto [absX, absY] = grid.getPositions(i, j);
  140. double x = (absX - mvp.x) * width / mvp.width;
  141. double y = (absY - mvp.y) * width / mvp.height;
  142. double w = width / mvp.width * gw;
  143. Texture* t = grid.getCell(i, j);
  144. if (t != nullptr) {
  145. t->drawRect(x, y, w, w);
  146. }
  147. else {
  148. this->empty.drawRect(x, y, w, w);
  149. }
  150. }
  151. }
  152. }
  153. MandelView::MandelView(mnd::Generator& generator, Gradient &gradient, MandelWidget* mWidget) :
  154. generator{ &generator },
  155. gradient{ gradient },
  156. mWidget{ mWidget }
  157. //context{ new QOpenGLContext(this) }
  158. {
  159. //context->setShareContext(mWidget->context()->contextHandle());
  160. hasToCalc.store(false);
  161. finish.store(false);
  162. }
  163. MandelView::~MandelView(void)
  164. {
  165. finish.store(true);
  166. condVar.notify_one();
  167. //calcThread.wait(100);
  168. calcThread.wait(100);
  169. calcThread.terminate();
  170. }
  171. void MandelView::setGenerator(mnd::Generator& value)
  172. {
  173. generator = &value;
  174. }
  175. void MandelView::start(void)
  176. {
  177. this->moveToThread(&calcThread);
  178. connect(&calcThread, SIGNAL(started()), this, SLOT(loop()));
  179. calcThread.start();
  180. }
  181. void MandelView::loop(void)
  182. {
  183. printf("thread!\n"); fflush(stdout);
  184. //QGLWidget* hiddenWidget = new QGLWidget(nullptr, mWidget);
  185. //hiddenWidget->setVisible(false);
  186. //hiddenWidget->context()->contextHandle()->moveToThread(&calcThread);
  187. //QOpenGLContext* context = hiddenWidget->context()->contextHandle();
  188. //context->setShareContext(mWidget->context()->contextHandle());
  189. //context->create();
  190. //printf("sharing: %d\n", QOpenGLContext::areSharing(hiddenWidget->context()->contextHandle(), mWidget->context()->contextHandle()));
  191. //fflush(stdout);
  192. //std::this_thread::sleep_for(std::chrono::milliseconds(3000));
  193. std::unique_lock<std::mutex> lock(mut);
  194. while(true) {
  195. printf("calcing!\n"); fflush(stdout);
  196. if (finish.load()) {
  197. break;
  198. }
  199. if (hasToCalc.exchange(false)) {
  200. const MandelInfo& mi = toCalc.load();
  201. auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
  202. generator->generate(mi, fmap.pixels.get());
  203. auto* bitmap = new Bitmap<RGBColor>(fmap.map<RGBColor>([&mi, this](float i) {
  204. return i >= mi.maxIter ? RGBColor{ 0, 0, 0 } : gradient.get(i);
  205. }));
  206. /*return i >= mi.maxIter ?
  207. RGBColor{ 0,0,0 } :
  208. RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127),
  209. uint8_t(sin(i * 0.01f) * 127 + 127),
  210. uint8_t(i) }; }));//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
  211. */
  212. //hiddenWidget->makeCurrent();
  213. //Texture* tex = new Texture(bitmap);
  214. //hiddenWidget->doneCurrent();
  215. //Texture* tex = 0;
  216. emit updated(bitmap);
  217. }
  218. printf("finished calcing!\n"); fflush(stdout);
  219. condVar.wait(lock);
  220. printf("waking!\n"); fflush(stdout);
  221. }
  222. }
  223. void MandelView::adaptViewport(const MandelInfo mi)
  224. {
  225. //bmp->get(0, 0) = RGBColor{ 10, uint8_t(sin(1 / vp.width) * 127 + 127), 10 };
  226. /*printf("adapted\n");
  227. if (calc.valid()) {
  228. auto status = calc.wait_for(std::chrono::milliseconds(0));
  229. if (status == std::future_status::deferred) {
  230. printf("deferred\n");
  231. } else if (status == std::future_status::timeout) {
  232. printf("timeout\n");
  233. } else if (status == std::future_status::ready) {
  234. printf("ready!\n");
  235. }
  236. }*/
  237. /*if (!calc.valid() || calc.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
  238. toCalc = mi;
  239. hasToCalc = true;
  240. calc = std::async([this, mi] () {
  241. QGLWidget* hiddenWidget = new QGLWidget(nullptr, (QGLWidget*) mWidget);
  242. QOpenGLContext* context = hiddenWidget->context()->contextHandle();
  243. hiddenWidget->makeCurrent();
  244. //context->setShareContext(mWidget->context()->contextHandle());
  245. //context->create();
  246. printf("sharing: %d\n", QOpenGLContext::areSharing(context, mWidget->context()->contextHandle()));
  247. fflush(stdout);
  248. //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  249. do {
  250. auto fmap = Bitmap<float>(mi.bWidth, mi.bHeight);
  251. generator->generate(mi, fmap.pixels.get());
  252. auto bitmap = fmap.map<RGBColor>([&mi](float i) { return i > mi.maxIter ?
  253. RGBColor{ 0,0,0 } :
  254. RGBColor{ uint8_t(cos(i * 0.015f) * 127 + 127),
  255. uint8_t(sin(i * 0.01f) * 127 + 127),
  256. uint8_t(i) }; });//uint8_t(::sin(i * 0.01f) * 100 + 100), uint8_t(i) }; });
  257. Texture* tex = new Texture(bitmap, context);
  258. //Texture* tex = 0;
  259. emit updated(tex);
  260. } while(hasToCalc.exchange(false));
  261. });
  262. }
  263. else {*/
  264. //std::unique_lock<std::mutex> lock(mut, std::try_to_lock);
  265. toCalc = mi;
  266. hasToCalc.exchange(true);
  267. condVar.notify_one();
  268. //}
  269. }
  270. MandelWidget::MandelWidget(mnd::MandelContext& ctxt, QWidget* parent) :
  271. QOpenGLWidget{ parent },
  272. mndContext{ ctxt },
  273. mv{ ctxt.getDefaultGenerator(), gradient, this }
  274. {
  275. this->setContentsMargins(0, 0, 0, 0);
  276. this->setSizePolicy(QSizePolicy::Expanding,
  277. QSizePolicy::Expanding);
  278. QObject::connect(&mv, &MandelView::updated, this, &MandelWidget::viewUpdated, Qt::AutoConnection);
  279. QObject::connect(this, &MandelWidget::needsUpdate, &mv, &MandelView::adaptViewport, Qt::DirectConnection);
  280. /*if (!ctxt.getDevices().empty()) {
  281. if (auto* gen = ctxt.getDevices()[0].getGeneratorDouble(); gen) {
  282. mv.setGenerator(*gen);
  283. }
  284. }*/
  285. }
  286. MandelWidget::~MandelWidget()
  287. {
  288. }
  289. void MandelWidget::initializeGL(void)
  290. {
  291. this->context()->functions()->glClearColor(0, 0, 0, 0);
  292. glDisable(GL_DEPTH_TEST);
  293. // looks not even better
  294. glDisable(GL_FRAMEBUFFER_SRGB);
  295. //glShadeModel(GL_SMOOTH);
  296. /*CpuGenerator<double> cpg;
  297. MandelInfo mi;
  298. mi.bWidth = this->width();//ql.geometry().width();
  299. mi.bHeight = this->height(); //ql.geometry().height();
  300. mi.maxIter = 250;
  301. mi.view = viewport;
  302. auto bitmap = cpg.generate(mi);*/
  303. Bitmap<RGBColor> bitmap(1, 1);
  304. bitmap.get(0, 0) = RGBColor{50, 50, 50};
  305. v = std::make_unique<MandelV>(context());
  306. tex = std::make_unique<Texture>(bitmap, context());
  307. mv.start();
  308. requestRecalc();
  309. }
  310. void MandelWidget::paintGL(void)
  311. {
  312. /*if (!initialized) {
  313. emit needsUpdate(viewport);
  314. initialized = true;
  315. }*/
  316. int width = this->width();
  317. int height = this->height();
  318. /*CpuGenerator<double> cpg;
  319. ClGenerator clg;
  320. MandelGenerator& mg = cpg;
  321. MandelInfo mi;
  322. mi.bWidth = width;
  323. mi.bHeight = height;
  324. mi.maxIter = 5000;
  325. mi.view = viewport;*/
  326. //auto bitmap = mg.generate(mi);
  327. /*Bitmap<RGBColor> bitmap(1000, 1000);
  328. for (int i = 0; i < 1000 * 1000; i++)
  329. bitmap.pixels[i] = RGBColor{5, uint8_t((i % 1000) ^ (i / 1000)), 50};
  330. tex = std::make_unique<Texture>(bitmap);*/
  331. glViewport(0, 0, width, height);
  332. glMatrixMode(GL_PROJECTION);
  333. glLoadIdentity();
  334. #ifdef QT_OPENGL_ES_1
  335. glOrthof(0, width, height, 0, -1.0, 1.0);
  336. #else
  337. glOrtho(0, width, height, 0, -1.0, 1.0);
  338. #endif
  339. glMatrixMode(GL_MODELVIEW);
  340. glClear(GL_COLOR_BUFFER_BIT);
  341. glLoadIdentity();
  342. tex->drawRect(0, 0, width, height);
  343. //v->empty = std::move(*tex)
  344. //v->empty.bind();
  345. v->paint(this->viewport);
  346. //*tex = std::move(v->empty);
  347. if (rubberbandDragging)
  348. drawRubberband();
  349. printf("painted GL\n");
  350. }
  351. void MandelWidget::drawRubberband(void)
  352. {
  353. glColor3ub(10, 200, 10);
  354. glBegin(GL_LINE_LOOP);
  355. glVertex2d(rubberband.x(), rubberband.y());
  356. glVertex2d(rubberband.right(), rubberband.y());
  357. glVertex2d(rubberband.right(), rubberband.bottom());
  358. glVertex2d(rubberband.x(), rubberband.bottom());
  359. glEnd();
  360. }
  361. void MandelWidget::zoom(float scale)
  362. {
  363. viewport.zoomCenter(scale);
  364. requestRecalc();
  365. }
  366. void MandelWidget::setMaxIterations(int maxIter)
  367. {
  368. this->maxIterations = maxIter;
  369. requestRecalc();
  370. }
  371. void MandelWidget::requestRecalc()
  372. {
  373. emit needsUpdate(MandelInfo{ viewport, this->width(), this->height(), maxIterations });
  374. }
  375. void MandelWidget::resizeGL(int width, int height)
  376. {
  377. glViewport(0, 0, (GLint) width, (GLint) height);
  378. }
  379. /*void MandelWidget::redraw(void)
  380. {
  381. /*CpuGenerator<double> cpg;
  382. MandelInfo mi;
  383. mi.bWidth = this->geometry().width();//ql.geometry().width();
  384. mi.bHeight = this->geometry().height(); //ql.geometry().height();
  385. mi.maxIter = 250;
  386. mi.view = viewport;*/
  387. //update();
  388. //emit needsUpdate(viewport);
  389. //auto bitmap = cpg.generate(mi).map<uint32_t>([](RGBColor rgb) { return 255 << 24 | rgb.b << 16 | rgb.g << 8 | rgb.r; });
  390. //}
  391. void MandelWidget::resizeEvent(QResizeEvent* re)
  392. {
  393. double aspect = double(geometry().width()) / geometry().height();
  394. //if (viewport.width > viewport.height * aspect)
  395. viewport.height = (viewport.width / aspect);
  396. //else
  397. // viewport.width = (viewport.height * aspect);
  398. requestRecalc();
  399. //redraw();
  400. }
  401. void MandelWidget::mousePressEvent(QMouseEvent* me)
  402. {
  403. rubberband.setCoords(me->x(), me->y(), 0, 0);
  404. rubberbandDragging = true;
  405. }
  406. void MandelWidget::mouseMoveEvent(QMouseEvent* me)
  407. {
  408. QRectF& rect = rubberband;
  409. float aspect = float(geometry().width()) / geometry().height();
  410. rect.setBottomRight(QPoint(me->x(), me->y()));
  411. if (rect.width() > rect.height() * aspect)
  412. rect.setHeight(rect.width() / aspect);
  413. else
  414. rect.setWidth(rect.height() * aspect);
  415. if (rubberbandDragging)
  416. emit repaint();
  417. }
  418. void MandelWidget::mouseReleaseEvent(QMouseEvent* me)
  419. {
  420. QRect rect = rubberband.toRect();
  421. QRect full = this->geometry();
  422. viewport.x += double(rect.left()) * viewport.width / full.width();
  423. viewport.y += double(rect.top()) * viewport.height / full.height();
  424. viewport.width *= double(rect.width()) / full.width();
  425. viewport.height *= double(rect.height()) / full.height();
  426. viewport.normalize();
  427. rubberbandDragging = false;
  428. requestRecalc();
  429. }
  430. void MandelWidget::viewUpdated(Bitmap<RGBColor>* bitmap)
  431. {
  432. if (bitmap != nullptr) {
  433. tex = std::make_unique<Texture>(*bitmap);
  434. delete bitmap;
  435. printf("viewUpdated\n");
  436. emit repaint();
  437. }
  438. }